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

一些常见面试题:

在这里插入图片描述

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

在这里插入图片描述

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

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

在这里插入图片描述

二、类装载器

(看做成快递员,把class文件(class文件开头有特定的文件标示cafe babe)字节码加载到内存,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于是否可以运行,由Execution Engine决定)
在这里插入图片描述
在这里插入图片描述

类装载器有哪几个?

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

加载器之间的层次关系:

在这里插入图片描述

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;

在这里插入图片描述

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());
    }
}

在这里插入图片描述

双亲委派机制:

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

当加载一个类会先到启动类加载器去找,找得到就用,找不到就到扩展类加载器找,找不到再去应用程序类加载器去找。(从顶层往下开始找)
在这里插入图片描述

双亲委派机制的理由?

是沙箱安全机制

例:

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

package java.lang;

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

运行结果:是无法被加载的在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

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

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

栈中主要存储3类数据:
本地变量:输入参数和输出参数,方法内的变量
栈操作:记录出栈、入栈的操作
栈帧数据:包括类文件、方法等。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

堆分为三部分:新生区,养老区,永久区
新生区分为:伊甸区、幸存者0区、幸存者1区
java8把永久区改为元空间
(物理上堆分为新生区、养老区;逻辑上分为新生区、养老区、元空间)
在这里插入图片描述

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

在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述

对象生命周期和GC

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

jvm堆参数调优:

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

-Xmn:一般不会调这个参数!
在这里插入图片描述
在这里插入图片描述

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

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

在这里插入图片描述
默认
-Xms:为物理内存大小的1/64
-Xmx:为物理内存大小的1/4
在这里插入图片描述

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

先查看内存大小:
在这里插入图片描述

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

在这里插入图片描述
运行:
在这里插入图片描述
上图: 新生代+老年代的内存大小等于981.5MB,元空间用的是物理内存空间!

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

分代收集算法:

  • 次数上频繁收集Young区
  • 次数上较少收集Old区
  • 基本不动元空间
    在这里插入图片描述
    在这里插入图片描述

GC 4个算法

在这里插入图片描述
1.引用计数法
在这里插入图片描述
2. 复制算法

发布了86 篇原创文章 · 获赞 69 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43736084/article/details/103937547
今日推荐