JVM学习杂记(一)
程序员文章站
2022-07-06 21:59:22
...
public class Example {
public static void main(String[] args) {
Demo demo = new Demo();
demo.getData();
}
}
class Demo{
public void getData() {
Boolean flag = true;
initData();
}
public String initData(){
return "init";
}
}
示例代码的在jvm中加载流程
注意:Demo.class被加载的时机(Example类main*()方法执行时,方法从被调用一直到执行结束,实际就是入栈和出栈的过程,程序计数器记录执行位置,堆存放了被创建的实例对象)
Q1:方法区的类会不会被回收
回收标准:首先需要该类的所有实例对象都已经从java的堆内存中里被回收
其次加载这个类的ClassLoader已经被回收
最后该类的class对象没有任何引用
Q2:jvm分代模型
年轻代,老年代,永久代
Q3:jvm对象内存分配和垃圾回收
对象首先优先分配在新生代,新生代如果内存满了,会触发Minor GC回收没人引用的垃圾对象,如果对象躲过15次(默认值,可修改),
就会进入老年代里,如果老年代也满了,就会触发Full GC,将老年代没人引用的垃圾对象清理
Q4:jvm内存相关的几个核心参数
-Xms:Java的堆内存的大小
-Xmx:Java堆内存的最大大小
-Xmn:Java堆内存中的新生代大小,扣除新生代剩下的就是老年代的内存大小
-XX:PermSize:永久代大小 --JDK1.8以后的版本被替换为-XX:MetaspaceSize
--XX:MaxPermSize:永久代最大大小 --JDK1.8以后的版本被替换为-XX:MaxMetaspaceSize
--Xss:每个线程的栈内存大小
java -Xms512M -Xmx512M -Xmn256M -Xss1M -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize:128M -jar App.jar
Q5.什么时候触发垃圾回收?被哪些变量引用的对象不能被回收呐?
新生代快满的时候,jvm会使用一种“可达性分析算法”判断哪些对象是可以被回收的,哪些对象是不可以被回收的,
可达性分析算法:对每个对象都分析一下谁在引用他,然后一层一层往上判断,看是否有一个GC Roots,局部变量和静态变量的引用
均可以看做一种GC Roots
Q6:Java对象不同的应用类型
强引用,软引用,弱引用,虚引用
Q6.1:没有GC Roots引用的对象,是一定马上被回收吗?
重写finalize()方法,因为对象被回收的时候,会先尝试调用它的finalize()方法,看是否把自己这个实例对象给了给某个 GC Roots变量
Q7:复制算法的优化
Eden区和Servivor区(1个Eden 80%,2个Survivor 20%)保证了90%的内存可用,Eden区快满了,触发垃圾回收,
第一次:Minor GC后会把Eden区中存活的对象都一次性转移到一块空着的Survivor区,接着Eden区就会被清空,然后再次分配新的
对象到Eden区,
下一次:Eden区满,再次触发Minor GC,就会把Eden区和放着上一次的Minor GC后存活的对象的Servivor区内的对象,转移到另外
一块Survivor区去;始终保持一块Survivor区是空着的,就这样循环使用这三块内存区域,10%的内存闲置和90%的内存使用
Q8:躲过15次Minor GC进入老年代
可以通过jvm参数 “-XX:MaxTenuringThreshold”来设置 默认是15次
Q8.1:动态对象年龄判断
定义另外的规则可以让对象进入老年代,不用等待15次GC后,假设说当前对象的Survivor区域里,一批对象的总大小大于这块Survivor
区域的内存大小的50%,那么大于等于的这部分对象可以直接进入老年代
Q8.2:大对象如何直接入老年代
有一个jvm参数,“-XX:PretenureSizeThreshold”,可以把他的值设置为字节数,比如1048576字节(1M),大于这个值对象均会
直接进入老年代之所以这么做就是为了避免新生代里面出现大对象屡次躲过GC,在两个Survivor区域里面来回复制多次之后进入老年代
Q8.3:Minor GC后的对象太多无法放入Survivor区怎么办?
发生Minor GC时,存活对象的大小超过Eden区,这时候必须把对象直接转移到老年代去(那万一老年代也放不下呐?),
首先在执行任何一次Minor GC之前,jvm都会先检查一下老年代的可用内存空间,
再检查是否大于新生代所有对象的的总大小(防止所有新生代所有对象全部进入老年代)。
假设执行Minor GC之前,发现老年代可用内存小于新生代的全部对象大小了,怎么办?
先检查“HandlePromotionFailure”参数是否设置了,如果有这个参数,
下一步判断,老年代的内存大小,是否大于之前每一次Minor GC后进入老年代的对象的平均大小,未设置“HandlePromotionFailure”,
就会触发一次Full GC(老年代垃圾回收的同时也会对新生代进行垃圾回收),就是先对老年代进行垃圾回收,再进行Minor GC,
Full GC过后,老年代依然没有足够的空间,就会OOM内存溢出
Q9:老年代回收算法
回收时机:Minor GC之前,检查发现很可能Minor GC之后进入老年代的对象太多,老年代放不下,此时提前触发Full GC,然后
顺带Minor GC;
Minor GC之后发现剩余太多对象,进入老年代且老年代也放不下
算法:标记整理算法(CMS)
Q10:Minor GC过后存在哪几种情况
第一种: Minor GC 过后,剩余的存活对象的大小,是小于Survivor区的大小,此时对象进入Survivor区
第二种: Minor GC过后,剩余的存活对象的大小 ,是大于Survivor区的大小,但是小于老年代可用内存大小的,此时就可以直接进入
老年代即可
第三种: Minor GC过后,剩余的存活对象的大小,大于了Survivor区域的大小,也大于老年代可用内存的大小,此时老年代都放不下
这些存活对象了,就会发生“Handle Promotion Failure”的情况,这个时候就会触发一次Full GC