jvm(2):垃圾收集和内存分配
typora-root-url: ./垃圾收集垃圾收集器关注的是线程共享的这部分内存。
回收方法区永久代的垃圾收集主要回收两部分内存:废弃常量和无用的类 废弃常量:没有任何对象引用该常量。 无用的类:同时满足
垃圾收集算法标记—清除算法Mark-Sweep分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。 效率问题:标记和清除两个过程的效率都不高。 空间问题:会产生大量不连续的内存碎片,可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存不得不提前触发另一次垃圾收集动作。 复制算法Copying将可用内存按容量划分大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。 代价是将内存缩小为了原来的一半。而且如果对象存活率较高,复制操作多,效率会变低。
标记—整理算法Mark-Compact首先标记出所有需要回收的对象,在标记完成后不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。 分代收集算法根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代。 新生代:复制算法 老年代:标记清除/标记整理算法 HotSpot的算法实现枚举根节点从GC Roots节点找引用链:如果要逐个检查引用,必然消耗时间。 GC停顿上:GC进行时必须暂停所有Java执行线程。 准确式GC:虚拟机可以知道内存中某个位置的数据的具体类型。 虚拟机应当有办法得知哪些地方存放的是对象的引用。 在HotSpot的实现中使用一组OopMap的数据结构来达到这个目的。 在类加载完成的时候,HotSpot就把对象内什么偏移量上什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置时引用。这样,GC在扫描时就可以直接得知这些信息了。 安全点Safepoint如果为每一条指令都生成OopMap,那将会需要大量的额外空间。 HotSpot没有为每条指令生成OopMap,只是在特定的位置记录了这些信息,这些位置被称为安全点。程序执行时并非在所有地方都能停顿下来进行GC,只有在到达安全点时才能暂停。 Safepoint的选定
在GC发生时让所有线程都跑到最近安全点在停顿下来的两种方案:抢先式中断和主动式中断
安全区域 Safe RegionSafepoint机制保证了程序执行时,在不太长的时间内就会进入到可进入的GC的安全点。 但是线程不执行时,比如处于sleep状态或者blocked状态时,线程无法响应jvm中断请求,走到安全的地方中断挂起,jvm也显然不太可能等待线程重新分配cpu时间。对于这种情况,使用安全区域来解决。 安全区域是指在一段代码片段之中,引用关系不会发生变化。在这个区域中的任意地方开始GC都是安全的。我们可以把安全区域看做是扩展了的安全点。 当线程执行到 Safe Region中的代码时,首先标识自己已经进入了 Safe Region,那样当在这段时间里JVM要发起GC时,就不用管标识自己为 Safe Region状态的线程了。当线程要离开安全区域时,它要检查系统是否完成了根节点枚举,如果完成了,那线程就继续执行,否则它就必须等待,直到收到可以安全离开 Safe Region的信号为止。 垃圾收集器Serial收集器单线程、独占式、Client模式下的默认新生代收集器 Stop The World:虚拟机在用户不可见的情况下把用户正常工作的线程全部停掉 内存分配方式:指针碰撞 ParNew收集器多线程并行[可以理解为Serial的多线程版本]、Server模式下的首选新生代收集器 应用程序仍会暂停 内存分配方式:指针碰撞 Parallel Scavenge收集器多线程、新生代收集器 目标是达到一个可控制的吞吐量[运行用户代码时间/CPU总时间]:注重吞吐量以及CPU资源 自适应调节策略 Serial Old收集器[Serial的老年代版本] 标记整理算法 Parallel Old回收器[Parallel Scavenge的老年代版本] 标记整理算法 CMS(Concurrent Mark Sweep)收集器并发收集、低停顿、对CPU资源敏感、无法处理浮动垃圾[并发清理阶段产生的垃圾]、并发阶段会降低吞吐量、老年代收集器 目标是获取最短回收停顿时间:重视服务器的响应速度 标记清除算法[产生大量空间碎片] 步骤:
内存分配方式:空闲列表 G1(Garbage-First)收集器并发与并行、分代收集、空间整合、可预测停顿 整体:标记整理算法;局部:复制算法;[不会产生内存空间碎片] 新生代和老年代不再是物理隔离了,它们都是一部分(可以不连续)Region。 G1跟踪各个Region里面的垃圾堆积的价值大小,在后台维护一个优先列表,优先回收价值最大的Region。细节方面,比如Region之间的对象引用要避免全堆扫描,虚拟机使用Remembered Set。G1中每个Region都有一个Remembered Set,虚拟机发现程序对引用类型的数据进行写操作时,产生一个中断,检查引用的对象是否处于不同的Region,如果是,就通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中。当进行内存回收的时候,在GC根节点的枚举范围中加入Remembered Set保证不全堆扫描。 步骤:
GC日志100.667: [Full GC [Tenured: 0K->210K(10240K),0.0149142 secs] 4603K->210K(19456K),[Perm: 2999K->2999K(21248K)],0.015007secs] [Times: user=0.01 sys=0.00,real=0.02secs] 虚拟机启动以来经过的秒数: [垃圾收集的停顿类型 [GC发生的区域: GC前该内存区域已使用容量->GC后该内存区域已使用容量(该内存区域总容量),GC所占用的时间] GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆总容量),[GC发生的区域: GC前该内存区域已使用容量->GC后该内存区域已使用容量(该内存区域总容量),GC所占用的时间] [具体时间数据: 用户态消耗的CPU时间 内核态消耗的CPU时间,操作从开始到结束所经过的墙钟时间] 垃圾收集器参数总结内存分配与回收策略对象优先在Eden分配大多数情况下,对象在新生代Eden中分配。当Eden中没有足够空间时,虚拟机将发起一次Minor GC。
大对象直接进入老年代大对象:需要大量连续内存空间的Java对象,比如数组。 经常出现大对象容易导致内存还有不少空间时就提前触发GC以获取足够的连续空间。 长期存活的对象将进入老年代如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor中,并且Age设为1。 对象在Survivor每读过一次Minor GC,Age就+1,当Age达到一定程度时(默认15),就将对象移到老年代。 如果在Survivor中相同年龄所有对象大小的综合>Survivor空间的一半,年龄>=该年龄的对象就可以直接进入老年代。 空间分配担保在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,则此次GC安全。 否则,虚拟机查看 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- java – Linux x86_64上的Cassandra Startup Error 1.2.6
- Redis 的安装与使用
- java – InterruptedException取消文件打开对话框 – 1.6.0
- 我如何引用一个接口在Java中实现的类类型?
- Error in event handler for "el.form.change":
- 是否有可能拥有一个普通的java库模块,取决于Android Studio
- java – 基于Spring Boot的测试中的上下文层次结构
- java – 下载驱动器视频并将其存储在本地设备中
- 记录java ftp下载图片只有96KB的问题
- 如何知道java SE类或方法是否是线程安全的?