Java虚拟机(JVM)学习笔记

不少组织都曾开发过Java虚拟机:

SUN公司曾经使用过3个虚拟机,Classic、Exact VM、Hotspot。
    其中Hotspot虚拟机沿用至今,并已被Oracle合并到其新的虚拟机平台中(与同样被Oracle收购的BEA公司的JRocket虚拟机合并)。
    IBM的Java虚拟机叫J9。
    Apache也曾推出过一款叫做Apache Harmony的Java运行平台,受到同样开源的OpenJDK的排挤,已于2011年退出市场。

Java虚拟机的基本结构:
    
    1、类加载子系统;
    2、Java内存(程序计数器、Java堆、Java栈、本地方法栈、方法区、直接内存);
    3、垃圾回收子系统;
    4、执行引擎。

Java堆:
    
    一个Java程序的几乎所有对象都存放在堆中,并且堆是完全自动化管理的,不需要显式释放堆中的垃圾对象。
    在不同的虚拟机中或在不同的垃圾回收机制下,Java堆可能具有不同的结构特征,但最常见的组织结构是把整个堆分为新生代和老年代(tenured)。
    新生代中存放新创建的对象和“年龄”不大的对象;老年代则存放“年龄”足够大的对象。
    新生代又分为eden、from、to三个区域,其中from和to是两块大小相等、可以互换角色的内存空间。
    通常,新创建的对象在eden区分配内存,在一次新生代回收后,如果对象还存活,则会被转移到from区或to区。
    对象进入from区或to区之后,没经过一次新生代回收,如果对象还存活着,则它的“年龄”加1。当“年龄”增长到一定程度后,会被转移到老年代。

Java栈:
    
    Java栈是线程私有的内存空间。函数调用是线程执行的基本行为,每次函数调用所需的数据都是通过Java栈传递的。
    Java栈和数据结构课程中所描述的栈有类似的特征,都是一种先进后出的数据结构,只支持入栈和出栈两种操作。
    栈帧是Java栈的基本单元。每次函数调用都会有一个栈帧入栈;每次函数调用结束,都会有一个栈帧出栈。
    无论函数是正常返回还是抛出异常,都会有一个栈帧出栈。

栈帧:
    
    在一个栈帧中,至少包含3个部分:局部变量表、操作数栈、帧数据区。
    由于每次函数调用都会有栈帧入栈,从而占据更多的栈空间,显然如果栈空间不足以放置更多的栈帧时,函数调用将无法继续下去,这个时候会抛出StackOverflowError。
    Java虚拟机提供了“-Xss”参数,用于在虚拟机启动的时候配置栈空间的最大值,这个最大值决定了函数嵌套调用的深度。

局部变量表:
    
    局部变量表是栈帧的重要组成部分,它用于保存函数的参数和局部变量。
    由于局部变量表在栈帧中,如果函数的参数和局部变量过多,会造成局部变量表膨胀,从而栈帧变大,函数嵌套调用的深度也会降低。

操作数栈:
    
    操作数栈也是栈帧的组成部分之一,它被用来保存计算的中间结果,作为计算过程中变量的临时存储空间。
    操作数栈也是先进后出的,很多Java字节码指令都需要通过操作数栈进行参数传递。

帧数据区:
    
    帧数据区中保存着常量池的指针,方便字节码指令访问常量池。
    帧数据区中保存了一张异常处理表,方便在发生异常时找到处理异常的代码。

栈上分配:
    
    栈上分配是Java虚拟机提供的优化技术。
    它的思路是,可以把某些线程私有对象分配在栈上,借助退栈操作直接清理该对象,而不需要垃圾回收子系统的介入,减少GC,从而提高系统性能。
    栈上分配的技术基础是逃逸分析,逃逸分析用于判断某个对象是否能够被其他线程访问到。
    如果某个对象能够被其他线程访问到,则它是一个逃逸对象,不能分配在栈上。
    顺便提一下,书上说只有Java虚拟机运行在Server模式下的时候,才可以启用逃逸分析。

方法区:
    
    方法区和Java堆一样,是线程共享的内存区域。它主要用于保存Java类信息(字段、方法等)。
    方法区的大小决定了虚拟机中可以加载多少Java类。
    如果有太多的Java类需要加载(通常使用了某些动态代理技术时,会动态产生过多的类信息),会抛出内存溢出错误。
    虚拟机参数-XX:PermSize和-XX:MaxPermSize用于指定方法区的最小和最大尺寸。默认情况下,最大64MB。
    在JDK1.6和1.7中,方法区也叫永久区。
    在JDK1.8、1.9和1.10中,永久区已被“元数据区”取代!
    元数据区是直接内存。
    和永久区不同的是,元数据区可以扩展至耗尽所有系统内存。

跟踪调试参数:
    
    Java虚拟机提供了大量用于跟踪系统状态的参数,配置使用这些参数,可以在Java程序运行过程中打印相关日志,用于问题分析。

GC参数:

GC(垃圾回收)机制是Java的一大特色,可以帮助程序员管理内存。
    但是当GC对程序本身造成影响(GC过于频繁、CPU过载等)时,需要进行分析处理。

打印GC日志:
    
    -XX:PrintGC,使用这个参数启动虚拟机,只要遇到GC,就会打印日志。
    -Xlog:gc,这个参数是用来给JDK9和JDK10打印GC日志的,因为这两个版本的JDK默认使用G1收集器。
    打印详细GC日志的参数是-XX:PrintGCDetails(JDK9和JDK10中对应的参数是-Xlog:gc*)。

跟踪类的加载、卸载:
    
    -verbose:class参数可以跟踪类的加载和卸载。
    单独使用-XX:+TraceClassLoading参数(JDK9和JDK10中对应的参数是-Xlog:class+load=info),跟踪类的加载情况。
    单独使用-XX:+TraceClassUnloading参数(JDK9和JDK10中对应的参数是-Xlog:class+unload=info),跟踪类的卸载情况。

查看系统参数:
    
    -XX:+PrintVMOptions参数,在程序运行时打印虚拟机接收到的命令行显式参数列表。
    -XX:+PrintCommandLineFlags参数,打印传递给虚拟机的显式和隐式参数。
    -XX:+PrintFlagsFinal参数,打印所有的系统参数值。

Java堆大小设置:
    
    当Java进程启动时,虚拟机会给该进程分配一块初始堆空间,当初始堆空间耗尽后,虚拟机会扩展堆空间,扩展后的堆空间大小不能超过最大堆空间。
    -Xms参数:在Java进程启动时,指定初始堆空间大小。
    -Xmx参数:指定最大堆空间大小。

新生代的配置:
    
    -Xmn参数:用于设置新生代的大小。
    -XX:SurvivorRatio参数用于指定新生代中eden区和from/to区的比例,即:
    -XX:SurvivorRatio=eden/from=eden/to
    新生代的大小一般设置为整个堆空间的四分之一到三分之一,新生代的大小对GC有很大影响!
    基本上,应该尽可能将对象预留在新生代,以减少老年代GC的次数。
    设置较大的新生代会减小老年的大小。
    -Xmn参数可以指定新生代的绝对大小,还可以使用-XX:NewRatio参数来设置新生代和老年代的比例:
    -XX:NewRatio=老年代/新生代

堆溢出:
    
    Java程序在运行过程中,如果遇到堆内存不足的情况时,则会抛出内存溢出错误(OOM,Out Of Memory)。
    对于OOM,Java虚拟机提供了-XX:+HeapDumpOnOutOfMemoryError参数,用于在发生OOM时导出整个堆信息。
    这个参数配合另一个参数-XX:HeapDumpPath,指定导出堆的存放路径:
    java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/java.dump ...
    另外,还有一个参数-XX:OnOutOfMemoryError,在发生错误时,调用一个脚本文件,用于善后处理。
    java -XX:OnOutOfMemoryError=d:/abc.bat ...

方法区配置:
    
    在JDK1.6和JDK1.7中,可以使用下面两个参数设置方法区大小的下限和上限:
    -XX:PermSize
    -XX:MaxPermSize
    JDK1.8以后,方法区已被废弃,类信息存放在元数据区中。
    元数据区只受系统可用内存的限制。
    可以使用-XX:MaxMetaspaceSize参数设置元数据区的最大可用值。

直接内存配置:
    
    直接内存跳过Java堆,直接操作系统原生内存。从一定程度上加快了内存的访问速度。
    但是认为使用直接内存一定可以提高性能是错误的。
    一般来说,直接内存的访问速度比Java堆内存要快,但是在内存申请时,直接内存毫无优势可言。
    所以看,直接内存适合申请次数较少、访问较频繁的场景。
    -XX:MaxDirectMemorySize参数可以限定直接内存的最大值,如果不设置这个参数,则默认最大值为-Xmx参数的值。
    当已使用的直接内存到达指定的最大值时,会触发GC,GC后如果仍旧内存不足,会引起OOM。

Java虚拟机的工作模式:
    
    Java虚拟机支持Client和Server两种运行模式。
    这两种运行模式可以通过两个参数分别指定:
    -client参数
    -server参数
    因为以Server模式启动Java程序时,会尝试手机更多的系统性能信息,使用更复杂的优化算法,所以Server模式启动程序的速度要慢于Client模式。
    但是也正是因为这样,以Server模式启动的Java程序,要远远快于以Client模式启动的Java程序。

常见垃圾回收算法:
    
    1、引用计数法(Reference Counting);
    2、标记清除法(Mark-Sweep);
    3、复制算法(Copying);
    4、标记压缩法(Mark-Compact);
    5、分代算法(Generationall Collecting);
    6、分区算法(Region)。

引用计数法:
    
    引用计数法是最经典且古老的一种垃圾收集算法。
    引用计数法的基本思想是给对象设置一个引用次数的变量。
    对象被赋值给其他变量,引用次数加一;引用失效后,引用次数减一。
    但引用次数变成零的时候,代表对象不再被任何变量引用,可以直接回收该对象占用的内存空间。
    引用计数法的最严重问题是无法处理循环引用的问题。

标记清除法:
    
    标记清除法是现代垃圾收集算法的思想基础。
    标记清除法分两个阶段:标记阶段和清除阶段。
    在标记阶段,算法通过根节点标记所有从根节点开始的可达对象。
    在清除阶段,回收所有未被标记的对象所占用的内存空间。
    标记清除法的最大问题是可能产生大量的内存碎片。

复制算法:
    
    复制算法的核心思想是:
    将这个内存空间分成两块,每次只使用其中的一块。
    当需要垃圾回收时,将正在使用的那一块中所有的存活对象都复制到另一块内存空间中,然后清理当前内存块中的所有对象。
    之后交换两块内存的角色,GC完成。
    复制算法非常符合新生代垃圾回收的要求,因为新生代里的垃圾对象通常远远多于存活对象,因此更有效率。
    之前介绍过新生代被分成了三块:eden、from、to。
    其中的from区和to区可以视为复制算法中的两块大小相同、地位相等且可以互换角色的内存空间。

标记压缩法:
    
    标记压缩法是一种老年代的回收算法。
    它是标记清除法的优化版。
    标记压缩法在执行了标记清除法的两个阶段后,还会把存活对象压缩到内存的一端,从而避免了碎片的产生。
    也就是说,标记压缩法=标记清除法+碎片整理。

分代算法:
    
    分代算法本身并不是一种新的垃圾回收算法。
    它的思路是,根据对象的特点,把内存分成不同的块,然后根据每块内存中对象的特点,选择不同的垃圾回收算法,以提高效率。
    分代算法被现代虚拟机广泛使用,几乎所有的垃圾回收器都区分新生代和老年代。
    通常,新生代会频繁发生垃圾回收,但是每次垃圾回收耗时都比较短;而老年代的垃圾回收并不频繁,但是只要发生垃圾回收,都会消耗很长时间。

分区算法:
    
    通常,内存空间越大,一次垃圾回收的耗时就越长。
    分区算法的思路是,把这个内存空间分割成若干小的区间,每次需要垃圾回收时,都选择其中的部分小区间进行回收,而不是回收整个内存空间。
    这样就有效控制了每次垃圾回收所产生的停顿时间。

可触及性:
    
    一个相对是否可以被当做垃圾回收,需要考察这个对象的可触及性,即在任何情况下,从根节点开始是否可以访问到这个对象。
    如果在任何情况下,从所有根节点开始,都无法直接或间接访问到这个对象,则说明这个对象已经不再被使用了,该对象可以被回收。
    可触及性设计三种状态:
    1、可触及,从根节点开始,可以到达该对象,则称该对象可触及;
    2、可复活,对象的所有引用都已释放,但是该对象可以通过调用其finalize方法被复活;
    3、不可触及,finalize方法已被调用(finalize只可能被虚拟机调用一次),但没有被复活,该对象就进入不可触及状态。

finalize方法:
    
    finalize方法是一个非常糟糕的反模式,不推荐使用它来释放资源。
    首先,finalize方法很可能在无意中复活对象。
    其次,finalize方法是由系统调用的,调用时机不明确。

引用强度:
    
    Java中提供了4个级别的引用:
    1、强引用;
    2、软引用;
    3、弱引用;
    4、虚引用。

强引用:
    
    强引用就是一般的引用,它不会被垃圾回收器回收。

软引用(java.lang.ref.SoftReference):
    
    软引用比强引用稍微弱一点。
    如果某对象只持有软引用,那么当堆空间不足时,就会被回收,因此软引用对象不会引起内存溢出。

弱引用(java.lang.ref.WeakReference):
    
    弱引用比软引用还要弱。
    在垃圾回收时,不管系统内存的使用情况如何,只要发现弱引用,就会立即对其进行回收。
    弱引用之所以可以在内存中存在,是因为垃圾回收线程的优先级很低,不能立即发现它。

虚引用(java.lang.ref.PhantomReference):
    
    虚引用是所有引用类型中最弱的一个。虚引用和没有引用几乎是一样的,随时都可能被回收。
    试图通过虚引用的get方法取得强引用时,总会失败。
    虚引用只是为了跟踪垃圾回收过程。

垃圾回收器:
    
    1、串行回收器;
    2、并行回收器;
    3、CMS回收器;
    4、G1回收器。

串行回收器:
    
    每次垃圾回收时,串行回收器都只有一个工作线程。
    对于并行能力差的机器,使用串行回收器反而性能更高、效率更好。因为机器并行能力差,启动多个线程并行回收,反而多了线程切换的开销。
    串行回收器是独占式的,垃圾回收过程中,所有其他线程都要停止工作。
    串行回收器既可以用于新生代,也可以用于老年代。

新生代串行回收器:
    
    新生代串行回收器使用复制算法,容易实现、逻辑处理高效,且没有线程切换的开销。
    虚拟机以Client模式启动时,串行回收器是默认回收器。

老年代串行回收器:
    
    老年代串行回收器使用标记压缩算法。
    可以使用以下三种参数指明老年代使用串行回收器:
    1、-XX:+UseSerialGC参数,新生代、老年代都使用串行回收器;
    2、-XX:+UseParNewGC参数(JDK9和JDK10中,该参数已作废),新生代使用ParNew回收器,老年代使用串行回收器;
    3、-XX:+UseParallelGC参数,指定新生代使用ParallelGC回收器,老年代使用串行回收器。

并行回收器:
    
    并行回收器使用多个线程同时进行垃圾回收,对于并行能力更强的机型来说,可以有效减少每次垃圾回收所需的时间。

新生代并行回收器(ParNew):
    
    ParNew回收器是新生代垃圾回收器,使用复制算法,它只是简单地将新生代串行回收器多线程化。
    ParNew回收器也是独占式的,当它启动时,其他所有线程都要暂停。
    -XX:+UseConcMarkSweepGC(JDK9、JDK10中已不建议再使用该参数),新生代使用ParNew回收器,老年代使用CMS回收器。
    ParNew回收器的线程数可以通过-XX:ParallelGCThreads参数指定。
    该回收器的线程数一般需要与计算机的CPU核数关联,一般情况下:
    1、当CPU核数小于8时,ParallelGCThreads的值取CPU的核数;
    2、当CPU核数大于8时,ParallelGCThreads的值等于3+(5*CPU_count/8)。

新生代并行回收器(ParallelGC):
    
    ParallelGC回收器也使用复制算法。
    看上去ParallelGC回收器和ParNew回收器很相似。不同的是,ParallelGC回收器非常关注系统的吞吐量。
    新生代ParallelGC回收器可以使用两种参数启用:
    1、-XX:+UseParallelGC,新生代使用ParallelGC回收器,老年代使用串行回收器。
    2、-XX:+UseParallelOldGC,新生代使用ParallelGC回收器,老年代使用ParallelOldGC回收器。
    ParallelGC回收器提供了两个参数用于控制系统吞吐量:
    1、-XX:MaxGCPauseMillis,设置最大停顿时间,该参数值是一个大于0的整数。
    指定该参数后,回收器会自动调整Java堆的大小或其他系统参数,尽量把停顿时间控制在MaxGCPauseMillis以内。
    MaxGCPauseMillis不能设置得过小,因为过小的话,回收器会把堆调整得很小,这样会造成垃圾回收频繁,系统吞吐量反而更低了。
    2、-XX:GCTimeRatio,设置吞吐量大小,该参数值是一个0到100以内的整数。
    系统将花费不超过1/(1+GCTimeRatio)的时间进行垃圾回收。
    默认GCTimeRatio到取值为99,即有不超过1/(1+99)=1%的时间用于垃圾回收。
    -XX:MaxGCPauseMillis参数和-XX:GCTimeRatio参数是互相矛盾的,因为:
    如果减小每次垃圾回收的时间,吞吐量也会降低;
    而增加吞吐量,又会延长每次垃圾回收的时间。
    ParallelGC回收器还可以通过-XX:+UseAdaptiveSizePolicy参数打开自适应GC策略,让虚拟机自行完成GC调优工作。
    
老年代并行回收器(ParallelOldGC):
    
    ParallelOldGC回收器使用标记压缩算法。
    ParallelOldGC回收器也是多线程的,和新生代ParallelGC回收器一样,它也关注吞吐量。

CMS回收器(Concurrent Mark Sweep、并发标记清除):
    
    CMS回收器使用标记清除算法。
    CMS回收器主要关注系统停顿时间。

CMS回收器的工作过程:
    
    CMS回收器工作时的主要步骤有:
    1、初始标记(独占式);
    2、并发标记;
    3、预清理;
    4、重新标记(独占式);
    5、并发清除;
    6、并发重置。

启用CMS回收器:
    
    使用参数-XX:+UseConcMarkSweepGC可以声明启用CMS回收器。

并行和并发的区别:
    
    并发是指回收器和应用线程交替执行;
    并行是指应用程序完全挂起,同时由多个线程一起执行GC。

CMS回收器的执行:
    
    CMS回收器不是独占式的,在CMS回收过程中,应用线程任然可以继续执行。
    应用线程执行过程中,又会产生垃圾对象,这些新的垃圾对象在当前的CMS回收过程中是无法清理的。
    所以在CMS回收过程中,要确保应用线程有足够的内存可用。
    因此,CMS回收器不会等到内存耗尽的时候才进行垃圾回收,而是在内存使用率到达一定程度时就开始垃圾回收了。
    可以通过参数-XX:CMSInitiatingOccupancyFraction指定一个阈值,默认68,即内存使用率到达68%时,CMS回收器就会执行。

CMS阈值的设置原则:

如果内存使用率飞速增长,在CMS回收过程中,已经出现了内存不足的状况,CMS回收就会失败。
    CMS失败后,虚拟机会启动老年代串行回收器进行垃圾回收,这时,应用线程会被全部挂起!造成很长的停顿时间。
    如果内存使用率上升缓慢,可以将CMSInitiatingOccupancyFraction设大点,以有效降低CMS的触发频率,减少老年代回收次数。
    如果内存使用率上升飞快,可以将CMSInitiatingOccupancyFraction设小点,以避免老年代串行回收器的触发频率。

CMS内存压缩参数:
    
    CMS回收器使用的是标记清除算法,这种算法容易产生内存碎片。
    为了减少碎片,CMS回收器提供了两个用于压缩内存的参数:
    1、-XX:+UseCMSCompactAtFullCollection,CMS垃圾收集完成后,进行一次碎片整理(非并发)。
    2、-XX:CMSFullGCBeforeCompaction,设定进行了多少次CMS回收后,进行一次内存压缩。

G1回收器(Garbage First):
    
    G1回收器是JDK1.7中引入的,可以替代CMS回收器。
    G1仍旧属于分代回收器。
    从堆的结构上看,G1回收器采用了分区算法,新生代和老年代都不必连续。
    G1兼顾新生代和老年代,其它回收器要么只工作在新生代,要么只工作在老年代。
    G1每次回收都会进行有效的内存复制,减少内存碎片。
    由于分区,G1可以每次只回收部分区块,缩小了回收范围,可以有效控制GC停顿。

G1相关参数:
    
    -XX:+UseG1GC,该参数用于声明使用G1回收器。
    -XX:MaxGCPauseMillis,该参数用于指定最大停顿时间。
    在实际运行过程中,如果任何一次停顿超过MaxGCPauseMillis,G1就会调整新生代和老年代的比例、调整堆大小、调整晋升年龄等,以符合预设目标。
    -XX:InitiatingHeapOccupancyPercent,这个参数用于指定当整个堆使用率达到多少时,触发并发标记周期。
    InitiatingHeapOccupancyPercent默认值是45,即当整个堆的使用率达到45%时,触发并发标记周期。
    InitiatingHeapOccupancyPercent一旦被设置,G1就不会再修改它了。

禁用System.gc():
    
    默认情况下,System.gc()会显式触发Full GC,同时对老年代和新生代进行回收。
    使用-XX:-+DisableExplicitGC,可以禁用显式GC,它使System.gc()等价于一个空函数调用。

调用System.gc()时,使用并发回收:
    
    默认情况下,如果没有禁用System.gc(),会使用传统的Full GC方式回收整个堆。
    使用参数-XX:+ExplicitGCInvokesConcurrent,可以使用并发方式进行回收。
    不使用这个参数,即使启用了CMS或G1,也不会进行并发回收。

TLAB(线程本地分配缓存,Thread Local Allocation Buffer):
    
    TLAB是线程专属的内存分配区域。
    在同一时刻,通常会有大量的线程在对上申请内存空间,所以每一次内存分配都必须进行加锁同步,加锁是效率低下的。
    TLAB由于是线程专属的,其他线程无法访问,所以能够不用加锁就进行内存分配。
    TLAB在eden区。

Java虚拟机(JVM)学习笔记(不定时更新)相关推荐

  1. Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论

    Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...

  2. Java虚拟机JVM学习05 类加载器的父委托机制

    Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...

  3. java虚拟机类加载机制_《深入理解java虚拟机》学习笔记一/类加载机制

    为何要读这本书? 近期看了左萧龙大哥的单例设计模式,后文讲解到了JVM对类实例化相关知识,感觉看着很无力,不懂,于是乎买本书研究下. 如何读? 个人水平一般,理解程度有限,书中说到每章关联不是很大,所 ...

  4. Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)

    Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...

  5. java帧结构_Java虚拟机运行时栈帧结构--《深入理解Java虚拟机》学习笔记及个人理解(二)...

    Java虚拟机运行时栈帧结构(周志明书上P237页) 栈帧是什么? 栈帧是一种数据结构,用于虚拟机进行方法的调用和执行. 栈帧是虚拟机栈的栈元素,也就是入栈和出栈的一个单元. 2018.1.2更新(在 ...

  6. 《深入理解java虚拟机》学习笔记之虚拟机即时编译详解

    郑重声明:本片博客是学习<深入理解java虚拟机>一书所记录的笔记,内容基本为书中知识. Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块 ...

  7. java之jvm学习笔记十三(jvm基本结构)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完 ...

  8. 《深入理解java虚拟机》学习笔记--第三章:垃圾收集器与内存分配策略

    GC所关心的问题就是: (1)那些内存需要回收? (2)何时回收? (3)怎么回收? 关注点:在程序计数器.java虚拟机栈.本地方法栈中,这些内存都是随着线程的创建而创建,销毁而销毁,这部分是不需要 ...

  9. 《深入理解java虚拟机》学习笔记四/垃圾收集器GC学习/一

    Grabage Collection      GC GC要完毕的三件事情: 哪些内存须要回收? 什么时候回收? 怎样回收? 内存运行时区域的各个部分中: 程序计数器.虚拟机栈.本地方法栈这3个区域随 ...

  10. 《深入理解JAVA虚拟机》——学习笔记

    JVM内存模型以及分区 JVM内存分为: 1.方法区:线程共享的区域,存储已经被虚拟机加载的类信息.常量.静态变量.即时编译器编译后的代码等数据 2.堆:线程共享的区域,存储对象实例,以及给数组分配的 ...

最新文章

  1. ROS中base_link, odom, fixed_frame, target_frame和虚拟大地图map的关系
  2. Hystrix 超时配置重写
  3. 丰富多彩的Android onTouch事件
  4. 前端项目课程7 banner设计注意事项
  5. 1024X768大图 (Wallpaper)
  6. libsvm使用方法总结
  7. IDEA Maven的下载和配置
  8. no target device found怎么解决_Linux 使用Unzip提示write error (disk full?)的解决方法
  9. linux查看python环境_运维笔记linux环境提示python: command not found hello
  10. 集合类 collection接口 LinkedList
  11. 利用哈希表设计快速电话号码查询系统
  12. MMA8452Q 三轴加速度传感器驱动
  13. ApplePay开发
  14. 计算机网络科研项目申请书,唐乾利:如何进一步完善医药类科研课题申请书
  15. Lesson 8 question 1 Dominator
  16. ESP32开发--使用NVS存储数据
  17. 华为ebgp_华为设备BGP详细配置
  18. mysql 写备注_mysql怎么添加备注
  19. React - review 2
  20. android 下载apk安装后自动启动,下载apk并启动安装

热门文章

  1. 淘宝直播如何设置录制回放功能,进行无人直播
  2. 成长一夏 挑战赛来袭 专属社区福利来袭~免费获得CSDN定制T恤衫
  3. 序列化对象为什么需要定义UID值
  4. Word转成PDF后有很多空白页怎么办?
  5. 加密pdf文档不能实现复制操作
  6. python爬网易云_如何用爬虫获取网易云音乐歌单中的歌曲?
  7. ANA* 路径规划算法基本原理
  8. No.4 clojure加载.clj文件
  9. supervisor启动worker源码分析-worker.clj
  10. 数据中心,云计算,大数据之间有什么区别和联系?