上篇地址 |
---|
jvm内存模型与垃圾回收(上) |
标记清除-标记整理-复制 这三个看上面的文章
将不同生命周期的对象采用不同的收集方式,以便提高回收效率
,一般是将Java堆分为新生代和老年代,这样可以根据各个年代的特点使用不同的回收算法,提高垃圾回收效率
标记清除-标记整理-复制 这三种算法在垃圾回收过程中,用户线程将处于 STW 状态,如果垃圾回收时间过长将严重影响用户体验
增量收集算法基本思想
垃圾收集线程只收集一小片区域的内存空间,接着切换到用户线程执行,依次反复,直到垃圾收集完成
。
总的来说,增量收集算法的基础仍是传统的标记清除,复制算法,增量收集算法通过对线程间冲突的妥善处理,允许垃圾收集线程以分阶段的方式完成标记、清理或复制工作
堆空间越大,一次GC时间就越长,为了更好的控制GC产生的停顿时间,将一块打的内存区域分割成多个小块,根据目标的停顿时间,每次合理地回收若干个小区间,而不是整个堆空间,从而减少一次GC所产生的停顿
分区算法将按照对象的生命周期长短划分成两个部分,分区算法将整个堆空间划分成连续的不同小区间,每一个区间独立使用,独立回收。这种算法的好处是可以空间一次回收多个小区间
System.gc() 或 Runtime.getRuntime().gc()
的调用,会显示触发Full GC,同时对老年代和新生代进行回收内存泄漏
:只有对象不被程序用到了,但GC又不能回收他们的情况,才叫内存泄漏用户线程与垃圾收集线程同时执行
(但不一定是并行的,可能会交替执行),垃圾回收线程在执行时不会停顿用户程序的运行程序执行时并非在所有地方都能停顿下来开始GC,只有在特定的位置才能停顿下来开始GC,这些位置称为 "安全点"
Safe Point的选择很重要,如果太少可能导致GC等待的时间太长,如果太频繁可能导致运行时的性能问题
。大部分指令的执行时间都非常短暂,通常会根据是否具有让程序长时间执行的特征
为标准。比如:选择一些执行时间较长的指令作为
Safe Point,如:方法调用、循环跳转和异常跳转
安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的
对于一个普通对象,如果存在的引用关系,就会被回收,反之不回收,如Object obj = new Object();
软引用用来描述一些还有用,但非必需的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收
,如果这次回收还没有足够的内存,才会抛出内存溢出异常
软引用对象与软引用对象的最大不同
就在于,当GC在进行回收时,需要通过算法检查是否回收软引用对象,而对于弱引用对象,GC总是进行回收。弱引用对象更容易、更快被GC回收
虚引用是所有引用类型中最弱的一个。
持有虚引用的对象和没有引用几乎是一样的,随时都可能被垃圾回收器回收。
它不能单独使用,也无法通过虚引用来获取被引用的对象,当视图通过虚引用的get() 方法获取对象时,总是 null
为一个对象设置虚引用关联的唯一目的在于跟踪垃圾回收过程。比如:能在这个对象被收集器回收时收到一个系统通知
吞吐量:运行用户代码的时间占总运行时间(程序运行时间+内存回收的时间)的比例
暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间
内存占用:Java 堆区所占的内存大小
-XX:+PrintCommandLineFlags:查看命令行相关参数(包含使用的垃圾收集器)
使用命令行指令:jinfo -flag 相关垃圾回收器参数 进程ID
回收新生代
唯一的选择默认新生代垃圾收集器
,采用串行回收、复制算法 和 STW 机制
对内存进行回收老年代垃圾收集器,
采用串行回收、标记整理算法和STW机制
对内存进行回收Parallel Scavenge收集器配合使用
,也可以作为老年代CMS收集器的后背垃圾回收方案
简单高效
,因为没有线程交互的开销-XX:+UseSerialGC 参数可以指定年轻代和老年代
都使用串行垃圾收集器ParNew 可以理解为是 Serial 收集器的多线程版本,除了采用并行回收的方式执行内存回收外,两款垃圾收集器之间几乎没有任何区别。
ParNew收集器在年轻代中同样采用复制算法、STW
机制
除 Serial 外,只有 ParNew GC 能与 CMS 收集器配合工作
-XX:+UseParNewGC 指定年轻代使用并行收集器
-XX:ParallelGCThreads 限制线程数量,默认开启和CPU数据相同的线程数
Parallel Scavenge 收集器同样采用了复制算法、并行回收和 STW机制
可控制的吞吐量
适合在后台运算而不需要太多交互的任务,比如:批量处理,科学计算等应用程序
Parallel Old 收集器
采用了标记压缩算法
,同样也是基于并行回收和 STW机制
相关参数:
-XX:MaxGCPauseMillis
设置垃圾收集器最大停顿时间(即STW的时间),单位是毫秒
-XX:GCTimeRatio
垃圾收集时间占总时间的比例(=1/(N+1))
取值范围(0,99).默认 99
-XX:+UseConcMarkSweepGC
设置使用CMS收集器(会自动将-XX:+UseParNewGC打开,即:ParNew+CMS+SerialOld的组合)
-XX:CMSInitiatingOccupanyFraction
设置堆内存使用率的阈值,一旦到达该阈值,便开始进行回收 JDK5默认68,JDK6及以上默认92
JDK1.5时期推出,CMS(Concurrent-Mark-Sweep)收集器
采用标记清除算法,也会STW
。
CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间
仅仅只是标记出 GC Root能**直接关联**到的对象
直接关联对象开始遍历整个对象图的过程
,这个过程耗时较长
但不需要停顿用户线程
,可以与垃圾收集线程一起并发运行修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
,这个阶段的停顿时间会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短清理删除掉标记阶段判断的已经死亡的对象,释放内存空间
在并发标记阶段如果产生新的垃圾对象,CMS将无法将这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有及时被回收
,从而只能在下一次执行GC时释放这些之前未被回收的内存空间要确保应用程序线程有足够的内存可用,CMS当内存使用率到达某一阈值时,便开始进行回收
,以确保应用程序在CMS工作过程中依然有足够的空间支持应用程序运行
要是CMS运行期间预留的内存无法满足程序需要,就会出现一次’Concurrent Mode Failure’失败,这是虚拟机将启动后备预案:启用 SerialOld收集器进行垃圾收集
,这样停顿的时间就很长了标记整理会变动对象在内存中的地址,而并发清除阶段并发清除线程会与用户线程并发执行,会影响到用户线程使用的资源,所以并发清除更适合CMS
如果想最小化地使用内存和并行开销,请选择 Serial GC
如果想最大化应用程序的吞吐量,请选择 Parallel GC
如果想最小化GC的中断或停顿时间,请选择 CMS GC
G1主要针对配备多核CPU及大容量内存的机器,极高概率满足GC停顿时间的同时兼顾高吞吐量的性能特性
为了适应现在不断扩大的内存和不断增加的处理器数量
,进一步降低暂停时间,同时兼顾良好的吞吐量
堆空间分为若干个区域,这些区域中包含了逻辑上的年轻代和老年代
它同时兼顾年轻代和老年代
内存的回收是以区域作为基本单位
,Region(区域)之间是复制算法
,但整体上实际可看作是标记整理
,两种算法都可以避免内存碎片。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GCG1跟踪各个区域里面垃圾堆积的价值大小,在后台维护了一个优先队列,每次根据允许的收集时间,优先回收价值最大的区域,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率
相较于CMS,在用户程序运行过程中,G1垃圾收集产生的内存占用要比CMS高,
小内存应用上CMS的表现大概率优于G1,大内存应用上G1可以发挥优势。
对于堆中的大对象,默认直接会分配到老年代,如果它是一个短期存在的大对象,就会对垃圾收集器造成负面影响。于是G1划分了一个Humongous区,用来专门存放大对象,如果一个H区装不下一个大对象,那么G1会寻找连续的H区来存储
,为了能找到连续的H区,有时候不得不启动Full GC。G1的大多数行为都把H区作为老年代的一部分来看待
程序运行过程中不断创建对象到Eden区,当Eden区内存耗尽时,G1会进行一次年轻代的垃圾回收
YGC时,首先G1停止应用程序的执行(STW),G1创建回收集(Collection Set),回收集指需要被回收的内存分段的集合,年轻代回收过程的回收集包含Eden区和Survivor区的所有内存分段
导致G1 Full GC的原因:
固定年轻代的大小会覆盖暂停时间目标
Serial => Parallel => CMS => G1=> ZGC
魔数是Class文件的标识
,用于确定这个文件是否是能被虚拟机接收的合法的Class文件。如果Class文件不以CAFEBABE开头,JVM在进行文件校验时会抛出异常
编译的副版本号minor_version
,第3、4个字节是编译的主版本号major_version
指 class 文件所携带的辅助信息,如:class源文件名称