JVM
2024-12-27 08:51:14 0 举报
AI智能生成
jvm总结,包括G1,ZGC,CMS等各种垃圾收集器,以及常见问题
作者其他创作
大纲/内容
GC垃圾收集理论
垃圾回收要干什么
哪些内存需要回收
MethodArea方法区
堆,主要是对对堆进行垃圾回收
哪些内存不需要进行回收
PC 程序计数器
JVM Stack
Native Method Stack
什么时候回收
当新生代内存快满了之后就会回收
如何判断需要回收
引用记数法
每一个对象设置一个计数器,有引用则加一,没有引用则减一,为0代表没有引用
优缺点
优点:实现简单
缺点:无法解决对象直接循环引用的问题
可达性分析算法<font color="#e74f4c"><b>(Jvm使用)</b></font>
每个对象都会分析一下谁在引用它,一层一层往上分析,如果说有方法区的局部变量,或者类的静态变量引用,则存在GC ROOTs引用则不能回收,(类的示例不是GC ROOTs)<br>
GC ROOTs种类
jvm虚拟机栈引用的对象
本地方法栈引用的对象
方法区中的类静态变量引用的对象对象
方法区中的常量引用的对象
垃圾回收过程
对象在eden园区诞生,当eden区没有足够的空间时发生MinorGC<br>
MinorGC后腾出来的空间还不足以容纳大对象,则直接进入老年代,可以用参数设置大对象直接进入老年代<br>
平常只用eden和一块survivor区,当一次minorGC时,survivor中的对象复制到另一块survivor中,同时年龄<br>加一,当达到设置的年龄时进入老年代<br>
动态年龄判定:占survivor中一半以上年龄相等的对象可以直接进入老年代
发生MinorGC时会先检查老年代最大可用连续空间是否大于新生代所有对象总空间:<br> 如果大于:说明MinorGC安全,<br> 如果小于:<br> 判断是否允许担保失败:<br> 允许:判断老年代最大连续可用空间是否大于历次晋升到老年代对象的平均大小,如果大于则尝试MinorGC,否则FullGC<br> 不允许:触发FullGC
java中的各种引用
强引用
一个变量引用一个对象,只要是强引用类型的,垃圾回收绝对不会回收这个对象<br>
软引用
softReference
正常情况下不会回收该对象,但是如果进行垃圾回收,空间不足够用,哪怕有对象引用也会进行回收
弱引用
WeakReference
发生垃圾回收时候就会回收
虚引用
PhantomReference
垃圾收集算法
大致区分
标记清除算法
标记和清除效率都不高,而且容易产生大量的内存碎片
复制算法(年轻代使用)
传统
1.新生代会分为两个区间
2.创建的对象都会放在那个区间,很快那个区间就满了<br>
3.这个时候给对象分配内存,发现内存不够用了
4.会对不用的对象进行标记,然后把这些对象转移到空的区域里<br>
5.把原来的区间清空掉
这样做的坏处就是内存一分为二,内存使用率低
复制算法的优化
1.Eden:Suvivor:to 8:1:1
平常只用eden和一块survivor区<br>
eden区满了就会触发GC
就会把eden区和survivor区的对象复制到另一个survivor区上去,然后一次性把eden和另一块survivor区清空掉
循环使用这三个区域
优点:只有10%的空间是空余的
问题:如果10%的空间不足以保存对象怎么办? 晋升老年代<br>
标记整理算法(老年代使用)
让还有引用的存活对象尽量靠一起,减少内存碎片<br>
按年轻代老年代区分
新生代
标记清除算法
复制算法
老年代
标记整理算法
分代收集理论
新生代
serial
是最基础的垃圾收集器,单线程版本
<font color="#e0c431">parNew</font>
多线程并行垃圾回收,使用:-XX:UseparNewGC<br>
<font color="#e74f4c">Parallel Scavenge</font><br>
多线程并行垃圾回收,注重吞吐量,而不是单次STW时间,可以使用参数-XX:MaxGCPauseMillis设置期望的最大stw时间<br>
老年代
serial old
serial的老年代版本,单线程垃圾收集
<font color="#e0c431">CMS</font>
多线程并发垃圾回收,是第一个关注stw的垃圾收集器
使用三色标记算法
如何实现系统一边工作一边进行垃圾回收
<font color="#e74f4c">1.初始标记</font>
是stw的,因为只标记一些gc roots,所以这个阶段很快,
2.并发标记
这个阶段是和用户程序一起运行的,会进行整个引用链的扫描,就是对全部老年代对象追踪,看看被谁引用了<br>
<font color="#e74f4c">3.重新标记</font>
是stw的,由于并发标记阶段会产生漏标和多标,会在这个阶段进行一些校验
4.并发清理
标记为垃圾的对象进行清理
问题
1.可能导致cpu资源紧张
2.在并发清理阶段还是会有新对象产生,这些对象没有呗垃圾回收,会成为浮动垃圾,要等到下一次垃圾<br>回收的时候才能回收,<br>如果要进入老年代的对象大于可用内存,会抛出 concurrent mode fuilure,此时会调出serial垃圾收集器<br>进行stw式的单线程垃圾回收。
3.内存碎片问题:<br>不过他不仅仅只是使用标记清理算法,他有一个参数,默认是打开的,意思是每次fullgc前都会整理一下碎片,将存活对象整理在一起<br>
<font color="#e74f4c">Parallel Old</font>
Parallel Scavenge的老年代版本
是吞吐量优先的垃圾收集器
各种垃圾收集器
serial
serial old
parNew
CMS
ps
po
G1
ZGC
GC垃圾收集器
serial
serial old
parNew
CMS
ps
po
G1
特点
将堆拆分为一个个大小相等的region
可以同时回收年轻代和老年代
年轻代和老年代是逻辑上的概念
可以设置一个逻辑垃圾回收预期停顿时间
一个region可能属于年轻代,也可能属于老年代
如何对垃圾回收导致的停顿可控的
做到这一点就必须追踪到每一个region的回收价值,<br>回收价值:每个region有多少是垃圾,回收需要多少时间,可以回收多少<br>
G1把堆内存空间划分为一个个的region,就可以追踪到每一个<br>region的回收价值,可以评估出来哪些region回收能带来最大的价值,<br>在有限的时间回收更多的内存<br>
Humongous Region
一个对象如果超过了region的一半就会放入
新生代和老年代回收的时候就会<br>把humongous region回收调
倒地有多少rigion,每个rigion多大
是自动设置的,region大小默认是2的倍数,默认将堆划分为2048个分区
由新生代进入老年代
对象在新生代躲过了多次垃圾回收,达到了一定年龄则进入老年代。-XX:MaxTenuringThreshold
动态年龄判定:某一次gc后发现,同年龄的对象超过了survivor的50%<br>比如说:有1岁,2岁,3岁的对象,3岁的对象超过了s区的50%,则3岁的对象<br>全部进入老年代
注意,G1会提供Humongous Region区域来存放大对象,而不是让大对象直接进入<br>老年代
默认分布
新生代初始占比5%
新生代最大占比60%
默认将堆分为2048个分区
G1的GC是很智能的,不仅仅是看占满新生代空间的60%,而是会自动感知,比如说<br>新分配一些内存区域,这时候G1发现回收时间达到了预估的200ms,就会开启回收
ZGC
特点
低停顿时间,宣称低于10ms
支持大内存,8-4TB,未来支持16TB
并发收集
吞吐量高,接近ps
关键技术
读屏障
颜色指针
运作流程
region
和G1一样采用了region,但是不是<br>大小相等的rigion, 而且jdk11的region<br>是单代的,不像G1的区分新生代老年代,<br>直到jdk21才分代
Small Region
2Mb
Medium Region
32Mb
Large Region
可变大小,2*n倍
存在的问题
浮动垃圾
cpu占用过高
朝生夕死的对象无法及时回收
仅linux可用
类加载
类加载的过程
1. 加载(Loading)
获取该类的二进制字节流,这里是可以从各个地方加载的,比如说网络等,在内存中生成一个该类的Class对象
2. 链接(Linking)
1. 验证
校验class文件格式是否正确是否以0XCACFEBABE开头,校验符号引用是否正确
2. 准备
给类变量分配内存,并设置初始值(注意这里是赋初始值而不是默认值,比如private static Integer i=1;这里是赋值i=null)
3. 解析
虚拟机将常量池中的符号引用替换为直接引用
3. 初始化 (核心阶段Initializing)<br>
这一步开始执行初始化方法,执行java字节码。 会进行对象的初始化操作,如果初始化一个类的时候发现他的父类没被加载,会先初始化他的父类。<br>
类加载器
bootstrap classloader
启动类加载器,主要用来加载一些核心的类库
ext classloader
用来加载jdk ext包下的类
application classloader
用来加载自己编写的类
自定义类加载器
可以自定义进行加载
双亲委派机制
加载一个类的时候首先委托给他的父类进行加载,逐级向上委托,父类找不到再交由子类进行加载。
问题:
为什么要有双亲委派
是处于安全的考虑
防止重复加载同一个class
保证核心class不被篡改
如何打破 双亲委派
需要重写ClassLoader的loadclass方法
tomcat是如何打破双亲委派的
https://www.cnblogs.com/crazymakercircle/p/15554725.html
Java中的对象
对象的初始化过程
检查类是否加载
分配内存
指针碰撞法
空闲列表法
初始化零值
执行init方法
这一部会设置初始值,执行构造方法等
对象的内存布局
对象头
示例数据
对齐填充padding
Java内存区域的划分
按线程私有共享划分
线程私有
PC
JVM Stack
Native Method Stack
线程共享
Heap
<font color="#e0c431">字符串常量池</font>
Method Area
直接内存
不受JVM GC管理
Meta Space
<font color="#e74f4c">运行时常量池</font>
类常量池
按区域划分
<font color="#e0c431">Jvm Stack</font>
java虚拟机的栈,一个方法对应一个栈帧,和线程生命周期相同
Local Variable Table
基本数据类型
变量引用
Operand Stack
Dynamic Linking
Return address
<font color="#e0c431">Native Method Stack</font>
本地方法栈,c和c++的,java调用JNI method
<font color="#e0c431">PC</font>
程序计数器,可以看做当前线程执行字节码的行号指示器
RunTime Constant Pool
运行时常量池,常量池运行时就被扔在运行时常量池
Method Area方法区
这是一个逻辑上的概念
Perm Space(jdk < 1.8)
<font color="#e74f4c"><b>字符串常量位于运行时常量池,FullGC不会清理</b></font>
MetaData (jdk >= 1.8)
<font color="#e0c431"><b>字符串常量池位于堆,FullGC会清理</b></font>
Direct Memory
jvm可以直接访问的内核空间的内存(os管理的内存)
JVM故障处理
基础处理工具
可视化工具
JVisualVM
arthas
Jvm参数
-Xms<br>
堆大小
-Xmx
堆最大大小
-Xmn
新生代大小,扣除新生代就是老年代
-XX:PermSize
永久代大小
-XX:MaxPermSize
永久代最大大小
-Xss
每个线程栈大小
-XX:MaxTenurningThreshold
进入老年代年龄
-XX:PreTenureSizeThreshold<br>
多大的对象直接进入老年代
常见问题
内存分配担保
目的:为了避免频繁发生full gc
在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,<br>那么 Minor GC 可以确认是安全的。如果不成立的话虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败,<br>如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,<br>如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 设置不允许冒险,<br>那么就要进行一次 Full GC。
JVM内存溢出
堆
OutOfMemoryError: Java heap Space
-Xms:最大堆,-Xmx:最小堆,-Xmn:新生代
栈
虚拟机栈
StackOverFlowError
单个线程可使用内存-Xss
本地方法栈
OutOfMemoryError: unable to create new native thread
方法区
OutOfMemoryError: PermGen
-XX:PermSize -XX:MaxPermSize
直接内存
at sun.misc.Unsafe.allocateMemory(Native Method)
元空间
java.lang.OutOfMemoryError:Meta space
-XX:MetaSpaceSize -XX:MaxMetaSpaceSize
为什么要区分新生代和老年代
和垃圾回收有关,新生代创建的对象大部分都是创建完不需要长时间使用就销毁的,老年代对象需要长期存在
对象是如何在jvm里流转的
0 条评论
下一页