jvm详解、GC、堆内存参数调优

一些常见面试题:

jvm详解、GC、堆内存参数调优_第1张图片

JVM的位置(运行在操作系统上,与硬件没有直接的交互)

jvm详解、GC、堆内存参数调优_第2张图片

一、jvm体系结构(记住背下来)

运行时数据区:有亮色的有灰色的,灰色的就是占得内存非常小,几乎不存在GC垃圾回收,并且线程独占的,亮色的存在垃圾回收,并且所有线程共享。

jvm详解、GC、堆内存参数调优_第3张图片

二、类装载器

(看做成快递员,把class文件(class文件开头有特定的文件标示cafe babe)字节码加载到内存,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于是否可以运行,由Execution Engine决定)
jvm详解、GC、堆内存参数调优_第4张图片
jvm详解、GC、堆内存参数调优_第5张图片

类装载器有哪几个?

1-启动类加载器,负责加载%JAVA_HOME%\bin目录下的所有jar包,或者是-Xbootclasspath参数指定的路径;
2-扩展类加载器:负责加载%JAVA_HOME%\bin\ext目录下的所有jar包,或者是java.ext.dirs参数指定的路径;
3-应用程序类加载器:负责加载用户类路径上所指定的类库,如果应用程序中没有自定义加载器,那么次加载器就为默认加载器。

加载器之间的层次关系:

jvm详解、GC、堆内存参数调优_第6张图片

public class MyObject {
    public static void main(String []args){
        Object object = new Object();
        MyObject myObject = new MyObject();
        System.out.println(object.getClass().getClassLoader());
        System.out.println(myObject.getClass().getClassLoader());
    }
}
运行结果:启动类加载器来加载java自带的类,BootStrap是C++写的所以输出的null;

jvm详解、GC、堆内存参数调优_第7张图片

package JVM;

public class MyObject {
    public static void main(String []args){
        Object object = new Object();
        System.out.println(object.getClass().getClassLoader());

        System.out.println();
        System.out.println();
        System.out.println();

        MyObject myObject = new MyObject();
        System.out.println(myObject.getClass().getClassLoader());
        System.out.println(myObject.getClass().getClassLoader().getParent());
        System.out.println(myObject.getClass().getClassLoader().getParent().getParent());
    }
}

jvm详解、GC、堆内存参数调优_第8张图片

双亲委派机制:

双亲委派机制得工作过程:
1-类加载器收到类加载的请求;
2-把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器(bootstrap);
3-启动器加载器检查能不能加载(使用findClass()方法),能就加载(结束);否则,抛出异常,通知子加载器进行加载。
4-重复3;

当加载一个类会先到启动类加载器去找,找得到就用,找不到就到扩展类加载器找,找不到再去应用程序类加载器去找。(从顶层往下开始找)
jvm详解、GC、堆内存参数调优_第9张图片

双亲委派机制的理由?

是沙箱安全机制

例:

String类,String是java.lang包下的类,默认情况下是启动类加载器进行加载的。假设我也自定义一个String。现在你会发现自定义的String可以正常编译,但是永远无法被加载运行。
这是因为申请自定义String加载时,总是启动类加载器,不会是其他的加载器,也就是不会用应用程序加载器加载。

package java.lang;

public class String {
    public static void main(String[] args) {
        System.out.println(1111);
    }
}

运行结果:是无法被加载的jvm详解、GC、堆内存参数调优_第10张图片

三、本地方法栈(加载native方法,了解)

四、程序计数器(类似一个指针,一条指令执行完用来指向下一个指令 )

jvm详解、GC、堆内存参数调优_第11张图片
jvm详解、GC、堆内存参数调优_第12张图片

五、方法区(类的模板工厂)

jvm详解、GC、堆内存参数调优_第13张图片

六、java栈(灰色,线程私有,不存在gc )

栈中主要存储3类数据:
本地变量:输入参数和输出参数,方法内的变量
栈操作:记录出栈、入栈的操作
栈帧数据:包括类文件、方法等。
jvm详解、GC、堆内存参数调优_第14张图片
jvm详解、GC、堆内存参数调优_第15张图片
jvm详解、GC、堆内存参数调优_第16张图片
jvm详解、GC、堆内存参数调优_第17张图片

七、堆(一个jvm实例只存在一个堆空间,大小可以调节)

堆分为三部分:新生区,养老区,永久区
新生区分为:伊甸区、幸存者0区、幸存者1区
java8把永久区改为元空间
(物理上堆分为新生区、养老区;逻辑上分为新生区、养老区、元空间)
jvm详解、GC、堆内存参数调优_第18张图片

java7之前, java8把永久区换成了元空间()

jvm详解、GC、堆内存参数调优_第19张图片
jvm详解、GC、堆内存参数调优_第20张图片

详细版:复制 -> 清空 -> 交换

jvm详解、GC、堆内存参数调优_第21张图片

对象生命周期和GC

jvm详解、GC、堆内存参数调优_第22张图片

jvm详解、GC、堆内存参数调优_第23张图片
jvm详解、GC、堆内存参数调优_第24张图片

jvm堆参数调优:

-Xms:start起始内存
-Xmx:max最大内存

-Xmn:一般不会调这个参数!
jvm详解、GC、堆内存参数调优_第25张图片
jvm详解、GC、堆内存参数调优_第26张图片

java8中,永久代被移除,被元空间取代,元空间的本质和永久代类似。
元空间与永久代之间最大的区别在于:
永久带使用的JVM的堆内存,但是java8以后的元空间并不在虚拟机中而是使用本机物理内存

因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少的类的元数据就不再有MaxPermSize控制,而由系统的实际可用空间来控制。

jvm详解、GC、堆内存参数调优_第27张图片
默认
-Xms:为物理内存大小的1/64
-Xmx:为物理内存大小的1/4
jvm详解、GC、堆内存参数调优_第28张图片

jvm参数调优:实际-Xms和-Xmx大小必须一致,防止GC和应用程序争抢内存,理论值的峰值和峰度忽高忽低。

先查看内存大小:
jvm详解、GC、堆内存参数调优_第29张图片

package JVM;

public class heap {
    public static void main(String[] args) {
        long maxMemory = Runtime.getRuntime().maxMemory();//返回java虚拟机试图使用的最大内存容量
        long totalMemory = Runtime.getRuntime().totalMemory();//返回java虚拟机的总内存容量
        System.out.println("-Xmx:MAX_MEMORY = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
        System.out.println("-Xms:TOTAL_MEMORY = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");
    }
}

更改-Xms和-Xmx参数:-Xms1024m -Xmx1024m -XX:+PrintGCDetails

jvm详解、GC、堆内存参数调优_第30张图片
运行:
jvm详解、GC、堆内存参数调优_第31张图片
上图: 新生代+老年代的内存大小等于981.5MB,元空间用的是物理内存空间!

GC 垃圾收集机制(分代回收算法)

分代收集算法:

  • 次数上频繁收集Young区
  • 次数上较少收集Old区
  • 基本不动元空间
    jvm详解、GC、堆内存参数调优_第32张图片
    jvm详解、GC、堆内存参数调优_第33张图片

GC 4个算法

jvm详解、GC、堆内存参数调优_第34张图片

1.引用计数法(了解,不用)

jvm详解、GC、堆内存参数调优_第35张图片

2. 复制算法(年轻代中的GC,不会产生内存碎片速度快,但是消耗内存空间)

当Eden区满了,会回收,没有被回收的会和survivorFrom区没有被回收的使用复制算法复制到survivorTo区,然后from区和Eden区全部清空,然后from区和to去进行交换(from区变成to区,to区变成from区,所以说谁空谁是to),对象没熬过一次Minor GC年龄就会+1,当对象年龄达到默认设置的15
(-XX:MaxTenuring Threshold参数来设置)那么就会被送到老年代。
jvm详解、GC、堆内存参数调优_第36张图片
jvm详解、GC、堆内存参数调优_第37张图片
jvm详解、GC、堆内存参数调优_第38张图片
在这里插入图片描述
jvm详解、GC、堆内存参数调优_第39张图片在这里插入图片描述在这里插入图片描述

3.标记清除(老年代使用,一般由标记清除或者是标记清除与标记整理的混合实现。。。与复制算法比较:节约了内存空间,但是会产生内存碎片,标记和清除会扫描两次耗时严重)

jvm详解、GC、堆内存参数调优_第40张图片
jvm详解、GC、堆内存参数调优_第41张图片
当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理工作接下来便让应用程序恢复运行
在这里插入图片描述

4.标记压缩(标记完成后不会进行清除,而是所有存活对象都像一端移动,然后直接清除边界以外的内存,这样不会产生内存碎片,但是时间会更长)

原理:
jvm详解、GC、堆内存参数调优_第42张图片
工作中实际使用:(标记压缩,多次GC后才会进行清除)
jvm详解、GC、堆内存参数调优_第43张图片

GC算法小总结:

jvm详解、GC、堆内存参数调优_第44张图片

有没有最好的GC算法??(没有最好的算法,只有最适合的算法,所以采用分代收集,标记整理好但是耗时)

但是有个G1垃圾收集算法: Garbage First(G1) 垃圾收集器(目前就不往下深入学习了,这里留个问题,以后再学习)
jvm详解、GC、堆内存参数调优_第45张图片

在这里插入图片描述

你可能感兴趣的:(JVM)