欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

2-JVM内存结构

程序员文章站 2024-01-28 17:19:28
内存结构 方法区 JDK1.7 之前包含1.7 将方法区称为 Perm Space 永久代 JDK1.8之后包含1.8 将方法区称为 MetaSpace 元空间。 堆(分配内存会大一些) 分配对象、new 实例。 堆内存当中划分为两个区域:老年代和新生代。 如何去划分老年代和新生代,根据对象的年龄。 ......

内存结构

2-JVM内存结构

2-JVM内存结构

2-JVM内存结构

方法区

jdk1.7 之前包含1.7 将方法区称为 perm space 永久代

jdk1.8之后包含1.8 将方法区称为 metaspace 元空间。

堆(分配内存会大一些)

分配对象、new 实例。

堆内存当中划分为两个区域:老年代和新生代。

如何去划分老年代和新生代,根据对象的年龄。这个年龄是一个对象经过一次gc,如果还存在的话,年龄就加一。当年龄超过默认值(15)时,就会从新生代划分到老年代当中。

1.新生代(young)

  1. new object() ,实例化10个单位为1的对象 ===>>> 新生代分配
  2. 新生代内存不够用时,触发gc
  3. gc之后,释放空间,会存在空间碎片
  4. 这时又new一个对象,这时这个对象的单位是3;gc之后释放的空间不连续,导致新生代不够分配,又会再一次触gc
  5. gc的弊端就是会消耗线程资源,stop the world。

以上的设计显然是不合理的,重新设计之后

新生代new出来的对象是朝生夕死,将新生代划分为两个区域:eden区,survivor区。survivor区又划分为s0、s1两个区域;

新生代内存分配情况是:eden区80%,survivor区20%(s0:10%,s1:10%)。

如果刚new出来的对象太大,超过了新生代的eden区内存,会直接存入在老年代。

举例说明:

老年代:2g内存

新生代:1g内存

eden区800mb

s0,s1各100mb

这是new一个900mb的对象,会直接分配在老年代(old)里。

新生代(young)gc:minor gc

老年代(old)gc:major gc

  • eden区

所有刚刚new出来的对象,就会分配在eden区。

  • survivor区
  1. s0、s1永远有一块内存是浪费的,一块被使用;

  2. s0、s1两个区域互相转换身份,以空间的浪费换取内存空间的连续性;

  3. eden=80%;s0=10%;s1=10%;eden:s0:s1=8:1:1;

  4. 比如说直接new一个900mb的新对象,会直接在老年代(old)区进行分配;

  5. 如果新生代(young)区的young gc之后对象的年龄不断的+1+1+1 > 年龄15之后,会将该对象存放到老年代(old)区;

    假如这时新生代(young)区有120mb存活对象,s区不够放了,会跟老年代借20mb的空间存放,会触发担保机制,这20mb依旧还是属于老年代(old)管理的。

  6. 极端情况,如果有个对象超过老年代内存直接oom。

2.老年代(old)

如果老年代的内存不够用了,会触发 old gc 也可 称为 major gc。old gc会比较耗时。当然一旦触发了old gc(major gc)通常都会伴随着young gc(minor gc)

old gc(major gc)+ young gc(minor gc)+ metaspace gc(可以忽略它)= full gc

调优的原则:

避免触发full gc,换句话说避免触发old gc(major gc);如果要触发gc,尽量只触发young gc(minor gc)。

  1. 尽量减少gc次数
  2. 尽量只触发young gc(minor gc)
2-JVM内存结构

实操:

在idea中vm options设置jvm堆内存:-xms30m -xmx30m(设置堆内存30mb,最大30mb)

@restcontroller
@requestmapping("/test/jvm")
public class testjvmcontroller {
    list<authaccount> list = new arraylist<>();
    @getmapping("/jvmtest")
    public void jvmtest() {
        while (true) {
            list.add(new authaccount());
        }
    }
}

运行springboot程序后、在java安装目录中,找到bin文件夹下的jvisualvm工具(这个工具是jdk自带的),首先还要安装visual gc的插件才能查看到jvm gc运行时状况

之后等待程序运行后,打开这个jvisualvm工具就可以查看到jvm内存运行时的状况

2-JVM内存结构

如果堆内存中,没有可分配的内存空间了,就会报oom。

同理方法区metaspace也会报oom,设置jvm中方法区大小:-xx:metaspacesize=40m -xx:maxmetaspacesize=40m。

栈也会报oom,首先我们先测试栈的深度:

	// 通过递归操作
	public static long count = 0;

    public static void test(long i) {
        system.out.println(count++);
        test(i);
    }

    public static void main(string[] args) {
        test(count);
    }

2-JVM内存结构

通过测试我们发现,栈的默认深度是7000左右。之后就会报oom错误。

可以根据需求去调整栈的深度大小;

一个栈的深度大小,太大或太小都会有弊端,太小的话影响方法链调用的深度、太大的话在整个java进程当中它能够创建这样一个的线程的数量是有限的,如果太大会影响到其他线程创建栈的深度。

通过前人的经验来看,最佳值设置到5000左右就可以了。可以通过jvm参数去设置。