jvm(2):垃圾收集和内存分配
typora-root-url: ./
垃圾收集
垃圾收集器关注的是线程共享的这部分内存。
jvisualvm
用来监控JVM的运行情况,可以用它来查看和浏览Heap Dump、Thread Dump、内存对象实例情况、GC执行情况、CPU消耗以及类的装载情况。
回收方法区
永久代的垃圾收集主要回收两部分内存:废弃常量和无用的类
废弃常量:没有任何对象引用该常量。
无用的类:同时满足
- 该类所有的实例都已经被回收
- 加载该类的
ClassLoader
已经被回收 - 该类对应的
java.lang.Class
对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
垃圾收集算法
标记—清除算法Mark-Sweep
分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
效率问题:标记和清除两个过程的效率都不高。
空间问题:会产生大量不连续的内存碎片,可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存不得不提前触发另一次垃圾收集动作。
复制算法Copying
将可用内存按容量划分大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
代价是将内存缩小为了原来的一半。而且如果对象存活率较高,复制操作多,效率会变低。
例子:新生代:一个eden,两个survivor[from和to](提高GC的效率)
每次使用一个eden和一个survivor。
标记—整理算法Mark-Compact
首先标记出所有需要回收的对象,在标记完成后不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法
根据对象存活周期的不同将内存划分为几块。一般是把Java堆分为新生代和老年代。
新生代:复制算法
老年代:标记清除/标记整理算法
HotSpot的算法实现
枚举根节点
从GC Roots节点找引用链:如果要逐个检查引用,必然消耗时间。
GC停顿上:GC进行时必须暂停所有Java执行线程。
准确式GC:虚拟机可以知道内存中某个位置的数据的具体类型。
虚拟机应当有办法得知哪些地方存放的是对象的引用。
在HotSpot的实现中使用一组OopMap的数据结构来达到这个目的。 在类加载完成的时候,HotSpot就把对象内什么偏移量上什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置时引用。这样,GC在扫描时就可以直接得知这些信息了。
安全点Safepoint
如果为每一条指令都生成OopMap,那将会需要大量的额外空间。 HotSpot没有为每条指令生成OopMap,只是在特定的位置记录了这些信息,这些位置被称为安全点。程序执行时并非在所有地方都能停顿下来进行GC,只有在到达安全点时才能暂停。
Safepoint的选定
- 太少:让GC等待时间太久;
- 太多:增大运行时负荷。
- 选定目标:具有让程序长时间执行的特征。最明显的就是指令序列的复用,例如方法调用、循环跳转、异常跳转等。
在GC发生时让所有线程都跑到最近安全点在停顿下来的两种方案:抢先式中断和主动式中断
- 抢先式中断不需要线程代码主动配合,当GC发生时,首先把所有线程中断,如果发现线程中断的地方不在安全点上,就恢复线程,让他跑到安全点上。[现在几乎没有虚拟机实现采用抢先式中断]
- 主动式中断是当GC需要中断线程的时候,不直接对线程操作,仅仅简单的设置一个标志,各个线程执行时主动去轮询这个标志,发现中断标志为真时就自己中断挂起,轮询标志的地方和安全点是重合的,另外再加上创建对象需要分配内存的地方。
安全区域 Safe Region
Safepoint机制保证了程序执行时,在不太长的时间内就会进入到可进入的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资源敏感、无法处理浮动垃圾[并发清理阶段产生的垃圾]、并发阶段会降低吞吐量、老年代收集器
目标是获取最短回收停顿时间:重视服务器的响应速度
标记清除算法[产生大量空间碎片]
步骤:
- 初始标记:标记
GC Roots
能直接关联到的对象[速度很快] - 并发标记:进行
GC Roots Tracing
的过程[时间较长] - 重新标记:修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录[时间稍长]
- 并发清除
内存分配方式:空闲列表
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。
新生代GC(Minor GC):发生在新生代的GC,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。
老年代GC(Major GC / Full GC):发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC。Major GC的速度一般比Minor GC慢10倍以上。
大对象直接进入老年代
大对象:需要大量连续内存空间的Java对象,比如数组。
经常出现大对象容易导致内存还有不少空间时就提前触发GC以获取足够的连续空间。
长期存活的对象将进入老年代
如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor中,并且Age设为1。
对象在Survivor每读过一次Minor GC,Age就+1,当Age达到一定程度时(默认15),就将对象移到老年代。
如果在Survivor中相同年龄所有对象大小的综合>Survivor空间的一半,年龄>=该年龄的对象就可以直接进入老年代。
空间分配担保
在发生Minor GC之前,虚拟机会检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,则此次GC安全。
否则,虚拟机查看HandlePromotionFailure
是否允许担保失败,如果允许,则继续检查老年代最大的可用连续空间是否大于历次晋升到老年代的对象的平均大小,如果大于,则尝试进行一次Minor GC,尽管有风险(因为判断的是平均大小,有可能这次的晋升对象比平均值大很多)。如果小于,或者设置不允许担保失败,这时要进行一次Full GC。
jvm(2):垃圾收集和内存分配的更多相关文章
- 深入理解JVM(5)——垃圾收集和内存分配策略
1.垃圾收集对象 垃圾收集主要是针对堆和方法区进行. 程序计数器.虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收. 哪 ...
- Java Web 深入分析(12) JVM(2) 垃圾收集与内存分配
前言 java的内存分配和垃圾回收往往是影响系统性能和并发能力的主要因素,虚拟机提供许多的参数就是为了根据不同环境和请教下进行调优,没有最好的调优也没有固定的调优.需要我们深入的去了解jvm的各个垃圾 ...
- 了解JVM运行时的内存分配
了解JVM运行时的内存分配 前言 上文中,在介绍运行时数据区域中的 JAVA 堆时,提到了 JVM 中的堆,一般分为三大部分:新生代.老年代.永久代,本文将进一步了解运行时的内存分配情况. 正文 1. ...
- JVM读书笔记之垃圾收集与内存分配
1 概述 说起垃圾收集( Garbage Collection , GC ) ,大部分人都把这项技术当做 Java 语言的伴生产物.事实上, GC 的历史远远比 Java 久远,1960 年诞生于 M ...
- jvm高级特性(4)(内存分配回收策略)
JVM高级特性与实践(四):内存分配 与 回收策略 一. 内存分配 和 回收策略 1,对象内存分配的概念: 往大方向讲,它就是在堆上分配(但也可能经过JIT编译后被拆散为标量类型并间接地栈上分配), ...
- [jvm]垃圾回收与内存分配策略
一.垃圾回收算法 概述 JVM中,当创建的对象不再被使用的时候,此时我们认为他是无用的“垃圾”:在现代主流的商用jvm中,都是通过可达性分析来判断对象是否存活的.这个算法的基本思想是通过一系列“GCR ...
- 阿里面试官:小伙子,你给我说一下JVM对象创建与内存分配机制吧
内存分配机制 逐步分析 类加载检查: 虚拟机遇到一条new指令(new关键字.对象的克隆.对象的序列化等)时,会先去检查这个指令的参数在常量池中定位到一个类的符号引用,并且这个符号引用代表的类是否 ...
- JVM垃圾回收器、内存分配与回收策略
新生代垃圾收集器 1. Serial收集器 serial收集器即串行收集器,是一个单线程收集器. 串行收集器在进行垃圾回收时只使用一个CPU或一条收集线程去完成垃圾回收工作,并且会暂停其他的工作线程( ...
- 深入理解Java虚拟机二:垃圾收集与内存分配
垃圾收集:垃圾收集要完成三件事,包括哪些内存需要回收,什么时候回收及如何回收. 1.需要回收的内存判定:没有引用指向原先分配给某个对象的内存时,则该内存是需要回收的垃圾 Java垃圾收集器在对内存进行 ...
随机推荐
- python--终端工具之subprocess
一. subprocess.getstatusoutput import subprocess cmd = 'ifconfig' def cmds(cmd,print_msg=True): statu ...
- 本地连接mysql的url写法
一.jdbc:mysql:///中三条斜杠(///) 第三个/代表什么? jdbc:mysql:///testdatabase等同于 jdbc:mysql://localhost:3306/testd ...
- 关于文件中"wb"与"rb"的理解
“rb”,”wb”这两种方式在操作文件时,直接跳过了系统的编码方式,在windows系统中,用的编码为gbk: ①:with open(“a.txt”,”w”) as f1: F1.write(“aa ...
- javaScript中的异步编程模式
1.事件模型 let button = document.getElementById("my-btn"); button.onclick = function(event) { ...
- ASP.NET MVC 方法View返回的两种方式
1.参数为字符串类型 例如我们在地址栏输入http://localhost:56431/Test/Index,会查找TestController类下的Index方法并执行,如下图 当我们返回字符串类型 ...
- C#常规TcpListener
1.Xaml <Window x:Class="Server.MainWindow" xmlns="http://schemas.microsoft.com/win ...
- laravle中常见的数据库加密
// 1.md5加密 $str=md5('123456'); // 2.base64_decode加密 $str2=base64_encode('123456'); // 2.1 base64_解密 ...
- IntelliJ IDEA Ultimate 6.2 版本免费试用期过期后如何破解
今天早上一打开IntelliJ IDEA时弹出“InteliJ IDEA License Activation”界面,需要激活新的license才可以使用.下面直接使用Activation code进 ...
- Excel数据源增加时,渗透表如何刷新?
使用Excel制作渗透表的时候在选择数据源范围的时候不要选择有限区域!!!最好圈定列范围 问题:制作渗透表是,在选择数据区域时使用[Ctrl + A]或者选定有限的数据区域,从而导致当数据源增加时,渗 ...
- Hdu2099 整除的尾数
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2099 Problem Description 一个整数,只知道前几位,不知道末二位,被另一个整数除尽了 ...