JVM内存结构

2023-09-12  本文已影响0人  宏势

一、虚拟机历史

三大主流虚拟机:Sun 的 HotSpot VM、BEA公司的JRockit VM 和 IBM公司的J9 VM

二、JVM内存结构

主要包括堆区,方法区(元数据空间),虚拟机栈(JAVA线程栈),本地方法栈,程序计数栈


RuntimeDataArea.png

分代是为了解决提升垃圾回收的速度

字符串存在永久代中,容易出现性能问题和内存溢出。
类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

永久代会为 GC 带来不必要的复杂度,并且回收效率偏低

设置-XX:+PrintCommandLineFlags 参数,启动程序会先打印jvm启动参数

栈的特点

主要是局部变量和函数形参,以及对象的引用存放在栈中

堆的特点

除了栈,元空间,其余都在堆中

运行时常量池

运行时常量池原先是方法区的一部分,从JDK1.7开始运行时常量池从方法区中移出,放在堆中。常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享,提升性能。

String str = "te";
String str1 = "test";
String str2 = "te" + "st";
String str3 = "test";
String str4 = str+"st";
String str5 = "te"+"st";

String newStr1 = new String("test");
String newStr2 = new String("test");

System.out.println(str1 == str3);       //true   str1和str3字符串常量池"test"
System.out.println(str1 == str2);       //true
System.out.println(str1 == newStr1);    // false.
System.out.println(newStr1 == newStr2); //false
System.out.println(str2 == str4); // false
System.out.println(str2 == str5); // true

'equals'相等的字符串,常量池只有一份,堆中有多份

Integer为例, valueOf方法源码如下:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

当整数在[-128,127]的范围在自动装箱时会直接使用常量池里面(不存在会创建一个),当整数不在[-128,127]范围内时,就会在堆中创建对象

int i = 100;
Integer i1 = 100;        //范围是[-128,127]的数在自动装箱时加入常量池里面
Integer i2 = 100;
Integer i3 = new Integer(100);
Integer i4 = new Integer(100);
System.out.println(i==i1);      //true   包装类型跟基本类型比较都会先拆箱转成基本类型再比较,因此比较的是值,类似使用equals
System.out.println(i1==i2);     //true.  都指向常量池的100
System.out.println(i2==i3);     //false
System.out.println(i3==i4);     //false

案例分析(JDK1.8)

class Student{

    private static final String PREFIX = "STUDENT:";    //静态字符串常量,字符串常量池
    private String name;
    private int age;

    public Student(String n, int a) {
        this.name = n;
        this.age = a;
    }

    public void changeAge(int a) {
        this.age = a;
    }
}
public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        Student student = new Student("zhangsan", 12);
        int num = 13;
        student.changeAge(13);
    }
}

三、常用垃圾回收器

gc.png

垃圾回收器 分类 作用位置 使用算法 特点 适用场景
Serial 串行 新生代 复制算法 响应速度优先 适用于单CPU环境下的Client模式
ParNew 并行 新生代 复制算法 响应速度优先 多CPU环境Server模式下与CMS配合使用
Parallel 并行 新生代 复制算法 吞吐量优先 适用于后台运算而不需要太多交互的场景
Serial Old 串行 老年代 标记-压缩算法 响应速度优先 单CPU环境下的Client模式
Parallel Old 并行 老年代 标记-压缩算法 吞吐量优先 适用于后台运算而不需要太多交互的场景
CMS 并发 老年代 标记-压缩算法 响应速度优先 适用于互联网或B/S业务
G1 并行与并发 新生代、老年代 复制算法 标记-压缩算法 响应速度优先 面向服务端应用

GC日志打印配置

-Xms2G -Xmx2G -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -Xloggc:/logs/myGC.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/
-Xms2G -Xmx2G -Xlog:gc*=info,gc+heap=debug,gc+age=trace,safepoint:/logs/gc_%t.log:time,level,tags:filecount=5,filesize=20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/

从上面例子对比,可以看出,JDK1.8及更早版本,设置gc日志显得比较麻烦,JDK1.9开始,统一使用一个Xlog 参数来设置gc日志打印。

java -Xlog:help  //查看参数所有所有选项

格式如下:

-Xlog[:[selections][:[output][:[decorators][:output-options]]]]

官网:https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/tutorial-Xlog/html/index.html
https://docs.oracle.com/en/java/java-components/enterprise-performance-pack/epp-user-guide/printing-jvm-information.html
https://docs.oracle.com/en/java/javase/11/jrockit-hotspot/logging.html#GUID-33074D03-B4F3-4D16-B9B6-8B0076661AAF

上一篇 下一篇

猜你喜欢

热点阅读