05_JVM
2021-08-29 19:41:29 2 举报
AI智能生成
登录查看完整内容
JVM知识梳理,面试快速参考
作者其他创作
大纲/内容
大部分正常对象优先在新生代Eden区分配,当Eden区没有足够空间分配时,虚拟机发起一次Minor GC
对象优先在Eden区分配
最典型的大对象是那种很长的字符串以及数组
避免在Eden区和Survivor区之间大量的内存复制
-XX:PertenureSizeThreshold参数
大对象直接进入老年代
如果对象在Eden区出生并且经过第一次Minor GC后仍然存活,并且能够被Survivor区容纳的化,将被移动到Survivor区,对象年龄设为1
对象在Survivor区每熬过一次Minor GC,年龄就增加1,当它的年龄增加到一定程度时(15),就会被晋升到老年代
-XX:MaxTenuringThreshold参数设置
长期存活对象进入老年代
如果在Survivor空间中一批对象的大小大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代
保证长期存活对象尽早进入老年代
对象年龄动态判定
每次Minor GC之前,虚拟机会检查老年代最大可用连续空间是否大于新生代所有对象总空间大小,考虑到所有对象存活的场景
如果成立,Minor GC确保安全
如果不成立,虚拟机查看HandlePromotionFailure设置值是否允许担保失败
如果允许担保失败,继续检查老年代最大可用连续空间是否大于历次晋升到老年代对象的平均大小
如果大于历次晋升到老年代对象的平均大小,尝试进行一次Minor GC,尽管这次Minor GC有风险,失败的话进行一次Old GC
如果小于历次晋升到老年代对象的平均大小或者设置不允许冒险,Minor GC要改为进行一次Old GC
老年代的空间分配担保规则主要是考虑到Minor GC之后,存活对象大于Survivior空间大小,直接进入老年代的场景
老年代空间分配担保规则
内存分配
发生在新生代上,发生的比较频繁,执行速度快
特点
Eden 区空间不足
触发条件
Minor GC
发生在老年代上,较少发生,执行速度慢
老年代区域空间不足
空间分配担保失败
Old GC
Full GC
内存回收
内存分配及内存垃圾回收原理
内存相关
当前虚拟机栈中的局部变量表中的引用的对象
当前本地方法栈中局部变量表中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
GC Roots
可达性分析算法
垃圾收集器绝对不会回收
强引用
垃圾收集器在内存充足的时候不会回收,不足的时候会回收
软引用
垃圾收集器无论内存是否充足,都会回收
弱引用
和没有任何引用一样,任何时候都可能被回收
虚引用
回收对象引用类型
有GC Roots引用的对象一般不会被回收,但是如果是软引用或者是弱引用,可能会被回收
没有GC Roots引用的对象一般会被回收,具体要看有没有重写finalize()或者调用过finalize()方法
对象是否存活
将需要回收的对象标记起来
清除垃圾对象
原理
标记和清除的效率都不高
会产生大量的不连续的内存碎片,造成内存浪费
存在的缺陷
标记-清除
复制算法将内存区域划分成两块大小相等的区域,每次使用时都只用其中一块区域
发生垃圾回收时将存活的对象全部复制到未使用的区域,然后对之前的区域进行垃圾回收
简单高性,不会出现内存碎片问题
优点
内存利用率低
存活对象较多时效率会明显降低
新生代划分成三个区域,Eden区、Survivor区(from)和 Survivor区(to),比例是 8:1:1
始终保持一块Survivor内存区域为空,循环使用三块内存区域
10%的内存是闲置的,90%的内存都被使用,垃圾回收性能,内存碎片控制以及内存利用率都非常好
新生代使用优化后的复制算法
复制算法
原理和标记-清除算法类似,只是最后一步的清除改为将存活对象全部移动到一端,然后再将边界之外的内存全部回收
需要移动大量对象,效率不高
老年代使用的是标记-清理算法
标记-清理
垃圾收集算法
多线程垃圾回收机制
早期没有G1垃圾回收器,新生代使用的垃圾回收器,内部使用复制算法进行垃圾回收
Stop The World
存在的问题
ParNew 收集器
垃圾回收线程和系统运行线程尽量同时执行的模式
早期没有G1垃圾回收器,老年代使用的垃圾回收器,内部使用标记-清理算法进行垃圾回收
第一阶段,初始标记
第二阶段,并发标记
第三阶段,重新标记
第四阶段,并发清除
CMS 收集器
G1 收集器
垃圾收集器
垃圾收集
垃圾回收
性能调优
第一,大部分正常对象都优先在新生代的Eden区域分配内存
第二,创建新的对象时,发现新生代的Eden区域没有足够的内存空间,触发一次垃圾回收,Minor GC,尝试把那些没有被引用的垃圾对象回收
第三,每次Minor GC,会有一些对象躲过垃圾回收,对象的年龄加1,被移动到Survivor区(to),超过一定年龄,会被晋升到老年代区域
第四,如果老年代满了,也会触发老年代的垃圾回收,把老年代里没有被引用的垃圾对象清理
对象在JVM内存中如何分配,如何流转
什么情况下,JVM内存中的一个对象会被垃圾回收
JVM中有哪些垃圾回收算法,各个算法有哪些优点和缺点
新生代和老年代分别适合什么样的垃圾回收算法
JVM中有哪些常见的垃圾回收器,有什么特点
最新的G1垃圾回收器的工作原理,简单聊一聊
面试题精选
第一步,首先从“.java”文件编译成“.class”字节码文件
第二步,类加载器将“.class”字节码文件加载到JVM内存
第三步,JVM内部开始执行代码
整体运行原理
要明白,类是按需加载,代码用到这个类的时候才会加载
new一个对象时
调用一个类的静态方法时
直接操作一个类的static属性时
遇到new,getStatic,putStatic,invokeStatic这四条指令
使用java.lang.reflect进行反射调用时
初始化类时,如果父类没有初始化,先初始化父类,初始化父类还是要先加载父类
虚拟机启动时,用户指定的主类
类加载时机
加载、验证、准备、解析、初始化、使用、卸载
生命周期
将编译后的.Class静态文件转换到内存中(方法区),然后暴漏出来,让程序员能访问
加载
确保Class文件的字节流中包含的信息符合当前虚拟机规范,并且不会危害虚拟机自身的安全
验证
给类以及类变量分配内存,并且设置类变量的初始值,使用的是方法区的内存
准备
将class文件的常量池的符号引用替换为直接引用
可能发生在初始化阶段之前,也可能发生在初始化阶段之后,后者是为了支持Java的动态绑定
解析
为类的静态变量赋予程序中指定的初始值,执行静态代码块中的程序
初始化
类加载过程
C++实现,时虚拟机自身的一部分
负责将存放在<JRE_HOME>\\lib目录中的类库加载到虚拟机内存中
启动类加载器
Java实现,独立于虚拟机外部,并且全部都继承抽象类 java.lang.ClassLoader
负责将存放在<JRE_HOME>/lib/ext或者被java.ext.dir系统变量所指定路径中的所有类库加载到虚拟机内存中
扩展类加载器
负责加载用户类路径(ClassPath)上所指定的类库
应用类加载器
根据用户需求自己定义,需要继承自ClassLoader
自定义加载器
其它加载器
如果一个类加载器收到类加载请求,它首先不会去尝试加载这个类,而是把这个请求委派给父类加载器完成
只有当父加载器在自己的搜索范围内找不到指定类时(ClassNotFoundException),子加载器才会尝试自己去加载
机制
首先检查类是否被加载
若未加载,则调用父类加载器的loadClass方法
若该方法抛出ClassNotFoundException异常,则表示父类加载器无法加载该类,则当前类加载器调用findClass加载类
若父类加载器可以加载该类,直接返回Class对象
实现
保证Java类库中的类不受用户类影响,防止用户自定义一个类库中的同名类,引起问题
好处
破坏
双亲委派模型
类加载器
类加载机制
存储对象
大多数对象是存活周期极短的,比如方法内部创建的一些对象,方法执行结束
Eden区
Survivor区(from)
Survivor区(to)
默认比例,8 : 1 : 1
Survivor区的设计的意义主要是解决碎片化问题
新生代
少数对象是长期存活的,比如类变量长期引用的对象
长期存活的对象会进入老年代
一些大对象也会直接进入老年代
老年代
新生代和老年代的默认比例, 2 :1
垃圾回收相关
年轻代里的对象的特点是创建之后很快被回收,老年代里的对象的特点是长期存在
年轻代和老年代需要不同的垃圾回收算法来回收对象
为什么要区分新生代和老年代
堆
类信息
静态变量
final类型常量
Class文件的常量池(编译器生成的各种字面量和符号引用)会在类加载后被放入这个区域
符号引用
字面量
存储信息
运行时常量池
方法区(元空间)(永久代)
线程共享区域
虚拟机栈
本地方法栈
程序计数器
上述三个区域的生命周期和线程相同
线程私有区域
直接内存
运行流程梳理
内存结构(区域划分)
-Xms,堆内存的大小
-Xmx,堆内存的的最大大小
-Xmn,堆内存中新生代的大小
-XX:MetaspaceSize
-XX:PermSize,永久代的大小
-XX:MaxMetaspaceSize
-XX:MaxPermSize,永久代的最大大小
-Xss,线程栈内存大小
核心参数
JVM
收藏
收藏
0 条评论
回复 删除
下一页