关于JVM内存模型的理解和垃圾回收机制笔记(一)

JVM内存模型图

在这里插入图片描述

  • 栈:存放基本数据类型和引用(可理解为一个地址),实例方法。主要异常会有(StackOverFlowError,OutOfMemoryError).
    线程私有,生命周期与线程相同,每个方法执行时会创建一个栈帧结构。用于存放局部变量表,操作数栈,动态链接,方法出口。

  • 堆:(Heap)存放对象实例,所有对象的内存地址在此分配。
    它是线程共享的,生命周期与虚拟机相同。主要异常有(OutOfMemoryError)

  • 程序计数器:线程私有,特点:占用内存小,记录的是线程执行的行号,负责分支,循环,跳转,异常,线程恢复等

  • 方法区:线程共享,存储类加载信息(Class对象),常量,静态变量,主要异常(OutOfMenmoryError)

  • 本地方栈:为虚拟机使用到的native方法服务,主要异常会有(StackOverFlowError,OutOfMemoryError).
    当有native关键字时,表明java的作用区域达不到了,会去调用底层的C语言的方法库。 即进入本地方法栈,调用本地方法接口(JNI),JNI作用:拓展java的使用,融合不同的编程语言为java所用。

  • 虚拟机栈:为虚拟机执行java 方法(字节码)而服务。

  • 运行时常量区:属于方法区的一部分,存放编译期生成的各种字面量和符号引用。

举个例子:

public class Math {

    public static final Integer COASTAT = 666;

    public int compute() {//一个方法对应一块栈帧内存区域
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;
    }

    public static void main(String[] args) {
        Math math = new Math();
        math.compute();
        Math math2 = new Math();
        math2.compute();
        System.out.println("test");
    }
}

查看jvm指令 Javap -v Mathclass.
在这里插入图片描述
上图对应的是compute()方法中对应的代码块执行步骤。
iconst_1 将int类型常量1压入栈,
程序计数器加1,istore_1将int类型值存入局部变量1(也就是a中),
iconst_2 将int类型常量2压入栈 ,
istore_2将int类型值存入局部变量2(也就是b)中,
以此类推,int c = (a + b) * 10;这一步对应先做加法,再将3存入操作数栈,: bipush 10将10压进操作数栈,
imul 做乘法运算,得到30,
istore_3 将int类型值存入局部变量3(也就是c),
iload_3 从局部变量3中装载int类型值c=30

在这里插入图片描述

动态链接:

栈帧中保存了一个引用,相当于C语言中的指针,指向该方法在运行时常量池中的位置,通过运行时常量池的符号引用(指向堆),完成将符号引用转化为直接引用。
在这里插入图片描述
#4地址在常量池存放,在执行compute()方法时,是通过#4地址调用Class类的compute()方法、
在这里插入图片描述

堆区分类

堆区新生区主要有伊甸园区(Eden),幸存0区和幸存1区,也叫作From区和To区,
老年代(jdk8叫做元空间):这里的对象一直有被引用,在幸存区经过15次full gc 操作之后一直有指针指向,eg:静态变量,数据库连接池对象等。

在这里插入图片描述

在创建对象时, 对象优先在Eden区分配

大多数情况下,对象会在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机(执行引擎)会发起一次 Minor GC。Minor GC相比Major GC更频繁,回收速度也更快。通过Minor GC之后,Eden区中绝大部分对象会被回收,而那些存活对象,将会送到Survivor的From区(若From区空间不够,则直接进入Old区) 。
== 注意==:

  1. Minor GC是清理年轻代
  2. Major GC 是清理老年代。
  3. Full GC 是清理整个堆空间—包括年轻代和老年代。

Survivor区

Survivor区相当于是Eden区和Old区的一个缓冲,Survivor又分为2个区,一个是From区,一个是To区。每次执行Minor GC,会将Eden区中存活的对象放到Survivor的From区,而在From区中,仍存活的对象会根据他们的年龄值来决定去向。(From Survivor和To Survivor的逻辑关系会发生颠倒: From变To , To变From,目的是保证有连续的空间存放对方,避免碎片化的发生)

Survivor区存在的意义

如果没有Survivor区,Eden区每进行一次Minor GC,存活的对象就会被送到老年代,老年代很快就会被填满。而有很多对象虽然一次Minor GC没有消灭,但其实也并不会蹦跶多久,或许第二次,第三次就需要被清除。这时候移入老年区,很明显不是一个明智的决定。所以,Survivor的存在意义就是减少被送到老年代的对象,进而减少Major GC的发生。Survivor的预筛选保证,只有经历15次Minor GC还能在新生代中存活的对象,才会被送到老年代。

举个例子:模拟栈内存爆满的情况

package com.ztb.entity;

import java.util.ArrayList;

public class HeapTest {
    byte[]  a= new byte[1024*100];//创建一个100kb的数组

    public static void main(String[] args) throws InterruptedException {

        ArrayList<HeapTest> heapTests=new ArrayList<>();
        while(true){//死循环,模拟栈内存爆满
            heapTests.add(new HeapTest());//都是有引用的对象,不会被GC释放
            Thread.sleep(50);//休眠  防止系统卡死

        }
    }
}

打开JavaVisal VM查看Java进程

在这里插入图片描述
对象刚开始创建时存放在伊甸园区,内存满后做Minor GC,释放空间。但是这个程序中的对象都是有引用的,所以会在幸存区堆积 ,经过十五次FULL GC之后,老年代中会一直有堆积对象,直到栈内存爆满。程序抛出StackOverFlowError异常。
在这里插入图片描述

总结:JVM调优的目的

1.== 将转移到老年代的对象数量降低到最小,减少fullGC的次数==
2.== 减少full GC的执行时间==

原因:

做full GC时,会停掉当前应用线程的执行,影响程序的性能

在这里插入图片描述
打印GC日志,一个程序在几秒中之内就做了几次FULLGC,都是由于元空间不足导致的,很有可能是加载的类太多或者静态变量太多。
调优思路:在java程序启动之前,就更改元空间的设置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值