JVM内存模型详解

一、JVM整体结构及内存

除了堆、方法区线程共享,其余都是线程私有(程序计数器、java虚拟机栈、本地方法栈)。
  在这里插入图片描述

二、JAVA虚拟机栈

	java虚拟机栈也就是数据结构中的栈先进后出,只不过栈里面存的都是栈帧
	栈帧:每个方法在执行时都会创建一个栈帧。栈帧中存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中压栈到出栈的过程
 

## 1、局部变量表

局部变量表中存储了基本数据类型(boolean、byte、char、short、int、float、long、double)的局部变量(包括参数)、和对象的引用(String、数组、对象等),但是不存储对象的内容。局部变量表所需的内存空间在编译期间完成分配,在方法运行期间不会改变局部变量表的大小。
  局部变量表中数据存储在变量槽(Variable Slot),每个变量槽最大存储32位的数据类型。对于64位的数据类型(long、double),JVM 会为其分配两个连续的变量槽来存储
  看下局部变量表是不是编译期间分配内存:如下Test该类字节码文件

public class Test {
    private void get() {
        String a = "a";
        System.out.println(a);
        String b = "b";
    }
    private void get1() {
        {
            String a = "a";
            System.out.println(a);
        }
        String b = "b";
    }
}
 此时并没有启动程序,此时局部变量表已分配内存,普通方法与 static 方法在第 0 个槽位的存储有所不同。非 static 方法的第 0 个槽位存储方法所属对象实例的引用(此demo中的this)。

在这里插入图片描述
  
  再看下get1()方法局部变量表,注意slot中的下标并不是我们期望的0、1、2,此现象称为slot复用:为了尽可能的节省栈帧空间,局部变量表中的 Slot 是可以复用的。方法中定义的局部变量,其作用域不一定会覆盖整个方法。当方法运行时,如果已经超出了某个变量的作用域,即变量失效了,那这个变量对应的 Slot 就可以交给其他变量使用,也就是所谓的 Slot 复用。通过一个例子来理解变量“失效”
在这里插入图片描述

2、操作数栈

操作数栈的元素可以是任意的Java数据类型。方法刚开始执行时,操作数栈是空的,在方法执行过程中,通过字节码指令对操作数栈进行压栈和出栈的操作。通常进行算数运算的时候是通过操作数栈来进行的,又或者是在调用其他方法的时候通过操作数栈进行参数传递。操作数栈可以理解为栈帧中用于计算的临时数据存储区

3、动态链接
  动态链接是在程序运行期间完成的将符号引用替换为直接引用(就是调用hello()方法的符号引用转为直接内存过程,和类加载静态链接表示静态方法调用符号引用转直接引用)

    public static void main(String[] args) {
   		Test t=new Test();
   		t.hello();//动态连接,运行期间,符号引用转内存地址过程
    }

4、方法出口
  方法返回地址,列如上面demo,hello方法栈帧执行完方法出口记录main栈帧中地址回到main方法中位置,

三、程序计数器

一个线程的执行,是通过字节码解释器改变当前线程的计数器的值,来获取下一条需要执行的字节码指令,从而确保线程的正确执行。线程上下文切换,再次回到当前线程位置

四、堆

 堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为伊甸园(Eden)和幸存区(Survivor区);幸存区又分为From Survivor空间和 To Survivor空间.
 gc时用到的垃圾收集算法(复制、标记清楚、标记整理)

1、对象直接进入老年代
大对象直接进入老年代
   大对象就是需要大量连续内存空间的对象(比如:字符串、数组)。JVM参数 -XX:PretenureSizeThreshold 可以设置大
对象的大小,如果对象超过设置大小会直接进入老年代,不会进入年轻代,这个参数只在 Serial 和ParNew两个收集器下
有效。
比如设置JVM参数:-XX:PretenureSizeThreshold=2000000 (单位是字节) -XX:+UseSerialGC ,再执行下上面的第一
个程序会发现大对象直接进了老年代
为什么要这样呢?
为了避免为大对象分配内存时的复制操作而降低效率。
长期存活的对象将进入老年代
既然虚拟机采用了分代收集的思想来管理内存,那么内存回收时就必须能识别哪些对象应放在新生代,哪些对象应放在
老年代中。为了做到这一点,虚拟机给每个对象一个对象年龄(Age)计数器。

对象动态年龄判断
当前放对象的Survivor区域里(其中一块区域,放对象的那块s区),一批对象的总大小大于这块Survivor区域内存大小的50%(-XX:TargetSurvivorRatio可以指定),那么此时大于等于这批对象年龄最大值的对象,就可以直接进入老年代了,
例如Survivor区域里现在有一批对象,年龄1+年龄2+年龄n的多个年龄对象总和超过了Survivor区域的50%,此时就会
把年龄n(含)以上的对象都放入老年代。这个规则其实是希望那些可能是长期存活的对象,尽早进入老年代。对象动态年
龄判断机制一般是在minor gc之后触发的

2垃圾收集算法:
 标记-复制算法
  为了解决效率问题,“复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的
内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对
内存区间的一半进行回收。 
  
  在这里插入图片描述
标记-清除算法
算法分为“标记”和“清除”阶段:标记存活的对象, 统一回收所有未被标记的对象(一般选择这种);也可以反过来,标
记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象 。它是最基础的收集算法,比较简单,但是会带来
两个明显的问题:
  1. 效率问题 (如果需要标记的对象太多,效率不高)
  2. 空间问题(标记清除后会产生大量不连续的碎片
  在这里插入图片描述

标记-整理算法
  与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回
收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
 在这里插入图片描述

五、方法区

用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值