Java之JVM大纲
2022-05-06 10:56:59 43 举报
AI智能生成
Java之JVM大纲
作者其他创作
大纲/内容
描述一下jvm内存模型,以及这些空间的存放的内容
堆内存划分的空间,如何回收这些内存对象,有哪些回收算法?
如何解决线上gc频繁的问题
1. 查看监控,以了解出现问题的时间点以及当前FGC的频率(可对比正常情况看频率是否正常)
2. 了解该时间点之前有没有程序上线、基础组件升级等情况
3. 了解JVM的参数设置,包括:堆空间各个区域的大小设置,新生代和老年代分别采用了哪些垃
圾收集器,然后分析JVM参数设置是否合理。
圾收集器,然后分析JVM参数设置是否合理。
4. 再对步骤1中列出的可能原因做排除法,其中元空间被打满、内存泄漏、代码显式调用gc方法
比较容易排查。
比较容易排查。
5. 针对大对象或者长生命周期对象导致的FGC,可通过 jmap -histo 命令并结合dump堆内存文
件作进一步分析,需要先定位到可疑对象。
件作进一步分析,需要先定位到可疑对象。
6. 通过可疑对象定位到具体代码再次分析,这时候要结合GC原理和JVM参数设置,弄清楚可疑
对象是否满足了进入到老年代的条件才能下结论。
对象是否满足了进入到老年代的条件才能下结论。
描述一下class初始化过程
简述一下内存溢出的原因,如何排查线上问题
内存泄漏 VS 内存溢出
java.lang.OutOfMemoryError: ......java heap space..... 堆栈溢出,代码问题的可能性极大
java.lang.OutOfMemoryError: GC over head limit exceeded 系统处于高频的GC状态,而且
回收的效果依然不佳的情况,就会开始报这个错误,这种情况一般是产生了很多不可以被释放
的对象,有可能是引用使用不当导致,或申请大对象导致,但是java heap space的内存溢出
有可能提前不会报这个错误,也就是可能内存就直接不够导致,而不是高频GC.
回收的效果依然不佳的情况,就会开始报这个错误,这种情况一般是产生了很多不可以被释放
的对象,有可能是引用使用不当导致,或申请大对象导致,但是java heap space的内存溢出
有可能提前不会报这个错误,也就是可能内存就直接不够导致,而不是高频GC.
java.lang.OutOfMemoryError: PermGen space jdk1.7之前才会出现的问题 ,原因是系统的
代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、
或者通过动态代码加载等方法,导致常量池的膨胀
代码非常多或引用的第三方包非常多、或代码中使用了大量的常量、或通过intern注入常量、
或者通过动态代码加载等方法,导致常量池的膨胀
java.lang.OutOfMemoryError: Direct buffer memory 直接内存不足,因为jvm垃圾回收不
会回收掉直接内存这部分的内存,所以可能原因是直接或间接使用了ByteBuffer中的
allocateDirect方法的时候,而没有做clear
会回收掉直接内存这部分的内存,所以可能原因是直接或间接使用了ByteBuffer中的
allocateDirect方法的时候,而没有做clear
java.lang.StackOverflowError - Xss设置的太小了
java.lang.OutOfMemoryError: unable to create new native thread 堆外内存不足,无法为
线程分配内存区域
线程分配内存区域
java.lang.OutOfMemoryError: request {} byte for {}out of swap 地址空间不够
jvm有哪些垃圾回收器,实际中如何选择
简述一下Java类加载模型?
JVM8为什么要增加元空间,带来什么好处
1、字符串存在永久代中,容易出现性能问题和内存溢出。
2、类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢
出,太大则容易导致老年代溢出。
出,太大则容易导致老年代溢出。
3、永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
元空间的特点
1,每个加载器有专门的存储空间。
2,不会单独回收某个类。
3,元空间里的对象的位置是固定的。
4,如果发现某个加载器不再存货了,会把相关的空间整个回收。
堆G1垃圾收集器有了解么,有什么特点
1. G1的设计原则是"首先收集尽可能多的垃圾(Garbage First)"。因此,G1并不会等内存耗尽(串
行、并行)或者快耗尽(CMS)的时候开始垃圾收集,而是在内部采用了启发式算法,在老年代找
出具有高收集收益的分区进行收集。同时G1可以根据用户设置的暂停时间目标自动调整年轻
代和总堆大小,暂停目标越短年轻代空间越小、总空间就越大;
行、并行)或者快耗尽(CMS)的时候开始垃圾收集,而是在内部采用了启发式算法,在老年代找
出具有高收集收益的分区进行收集。同时G1可以根据用户设置的暂停时间目标自动调整年轻
代和总堆大小,暂停目标越短年轻代空间越小、总空间就越大;
2. G1采用内存分区(Region)的思路,将内存划分为一个个相等大小的内存分区,回收时则以分
区为单位进行回收,存活的对象复制到另一个空闲分区中。由于都是以相等大小的分区为单位
进行操作,因此G1天然就是一种压缩方案(局部压缩);
区为单位进行回收,存活的对象复制到另一个空闲分区中。由于都是以相等大小的分区为单位
进行操作,因此G1天然就是一种压缩方案(局部压缩);
3. G1虽然也是分代收集器,但整个内存分区不存在物理上的年轻代与老年代的区别,也不需要
完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都
可能随G1的运行在不同代之间前后切换;
完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念,或者说每个分区都
可能随G1的运行在不同代之间前后切换;
4. G1的收集都是STW的,但年轻代和老年代的收集界限比较模糊,采用了混合(mixed)收集的方
式。即每次收集既可能只收集年轻代分区(年轻代收集),也可能在收集年轻代的同时,包含部
分老年代分区(混合收集),这样即使堆内存很大时,也可以限制收集范围,从而降低停顿。
式。即每次收集既可能只收集年轻代分区(年轻代收集),也可能在收集年轻代的同时,包含部
分老年代分区(混合收集),这样即使堆内存很大时,也可以限制收集范围,从而降低停顿。
5. 因为G1建立可预测的停顿时间模型,所以每一次的垃圾回收时间都可控,那么对于大堆
(16G左右)的垃圾收集会有明显优势
(16G左右)的垃圾收集会有明显优势
介绍一下垃圾回收算法
Happens-Before规则
先行发生原则(Happens-Before)是判断数据是否存在竞争、线程是否安全的主要依据。
先行发生是Java内存,模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,
那么操作A产生的影响能够被操作B观察到。
先行发生是Java内存,模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,
那么操作A产生的影响能够被操作B观察到。
口诀:如果两个操作之间具有happen-before关系,那么前一个操作的结果就会对后面的一个操作可
见。是Java内存模型中定义的两个操作之间的偏序关系。
见。是Java内存模型中定义的两个操作之间的偏序关系。
常见的happen-before规则
1.程序顺序规则
一个线程中的每个操作,happen-before在该线程中的任意后续操作。(注解:如果只有一个线程的操
作,那么前一个操作的结果肯定会对后续的操作可见。)
程序顺序规则中所说的每个操作happen-before于该线程中的任意后续操作并不是说前一个操作必须要
在后一个操作之前执行,而是指前一个操作的执行结果必须对后一个操作可见,如果不满足这个要求那
就不允许这两个操作进行重排序
作,那么前一个操作的结果肯定会对后续的操作可见。)
程序顺序规则中所说的每个操作happen-before于该线程中的任意后续操作并不是说前一个操作必须要
在后一个操作之前执行,而是指前一个操作的执行结果必须对后一个操作可见,如果不满足这个要求那
就不允许这两个操作进行重排序
2.锁规则
对一个锁的解锁,happen-before在随后对这个锁加锁。(注解:这个最常见的就是synchronized方法和
syncronized块)
syncronized块)
3.volatile变量规则
对一个volatile域的写,happen-before在任意后续对这个volatile域的读。该规则在CurrentHashMap
的读操作中不需要加锁有很好的体现
的读操作中不需要加锁有很好的体现
4.传递性:
如果A happen-before B,且B happen-before C,那么A happen - before C.
5.线程启动规则
Thread对象的start()方法happen-before此线程的每一个动作。
6.线程终止规则
线程的所有操作都happen-before对此线程的终止检测,可以通过Thread.join()方法结束,
Thread.isAlive()的返回值等手段检测到线程已经终止执行。
Thread.isAlive()的返回值等手段检测到线程已经终止执行。
7.线程中断规则
对线程interrupt()方法的调用happen-before发生于被中断线程的代码检测到中断时事件的发生。
描述一下java类加载和初始化的过程?
加载,链接(验证,准备,解析),初始化,使用,卸载
加载
加载主要是将.class文件通过二进制字节流读入到JVM中。 在加载阶段,JVM需要完成3件事
1)通过classloader在classpath中获取XXX.class文件,将其以二进制流的形式读入内存。
2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构;
3)在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
链接
验证
主要确保加载进来的字节流符合JVM规范。验证阶段会完成以下4个阶段的检验动作
1)文件格式验证
2)元数据验证(是否符合Java语言规范)
3)字节码验证(确定程序语义合法,符合逻辑)
4)符号引用验证(确保下一步的解析能正常执行)
准备
准备是连接阶段的第二步,主要为静态变量在方法区分配内存,并设置默认初始值。
解析
解析是连接阶段的第三步,是虚拟机将常量池内的符号引用替换为直接引用的过程
初始化
初始化阶段是类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值。
当有继承关系时,先初始化父类再初始化子类,所以创建一个子类时其实内存中存在两个对象实
例。
当有继承关系时,先初始化父类再初始化子类,所以创建一个子类时其实内存中存在两个对象实
例。
使用
程序之间的相互调用。
卸载
即销毁一个对象,一般情况下中有JVM垃圾回收器完成。代码层面的销毁只是将引用置为null。
JVM线上出OOM问题了如何定位?
吞吐量优先和响应时间优先的回收器是哪些?
吞吐量优先
Parallel Scavenge+Parallel Old(多线程并行)
响应时间优先
cms+par new(并发回收垃圾)
什么叫做阻塞队列的有界和无界,实际中有用过吗
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列,线程池,生产者消费者
LinkedBlockingQueue:一个由链表结构组成的无界阻塞队列,线程池,生产者消费者
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列,可以实现精确的定时任务
DelayQueue:一个使用优先级队列实现的无界阻塞队列,可以实现精确的定时任务
SynchronousQueue:一个不存储元素的阻塞队列,线程池
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列
LinkedBlockingDeque:一个由链表结构组成的双向无界阻塞队列,可以用在“工作窃取”模式
jvm监控系统是通过jmx做的么
内存屏障的汇编指令是啥
怎么提前避免内存泄漏
0 条评论
下一页