资讯专栏INFORMATION COLUMN

JVM内存模型与运行时数据区域

canopus4u / 2537人阅读

摘要:内存模型和运行时数据区域的关系主内存对应着堆,工作内存对应着栈。在的单例模式中有运用到二运行时数据区域内存区域因为的运行时数据区域一直在改善,所以不同版本之间会有不同。

一、java内存模型

java定义内存模型的目的是:为了屏蔽各种硬件和操作系统的内存访问之间的差异。

java内存模型规定了所有的变量都存储在主内存中,每条线程拥有自己的工作内存,工作内存保存了主内存中变量的副本。

线程对变量操作只能在工作内存中进行,不能直接读写主内存的变量。

不同线程之间的变量访问需要通过主内存来完成。

1、java内存模型和java运行时数据区域的关系:主内存对应着java堆,工作内存对应着java栈。

2、volatile关键字,使得变量的更新在各个工作内存中都是实时可见的。在DCL的单例模式中有运用到!

二、java运行时数据区域/内存区域

因为jvm的运行时数据区域一直在改善,所以不同jdk版本之间会有不同。

1、jdk1.7之前的jvm内存区域,拥有永久代

1、程序计数器的作用,因为.java文件被编译成.class文件,它作为当前线程所执行的字节码的行号指示器。当字节码解释器工作时,就是通过改变这个计算器的值来选取下一条要执行的字节码指令。每条线程都有一个独立的程序计数器。

2、本地方法栈就是执行本地native方法的栈,native方法由虚拟机实现!

3、java虚拟机栈描述的是该线程执行java方法(method)时的内存模型。每一个方法都对应一个栈帧,栈帧中的局部变量表存储了方法中的基本数据类型变量、对象引用变量。

如上图所示,局部变量表保存了方法中声明的8种基本类型变量和对象引用变量。每一个栈帧中还有一个指向运行时常量池的引用,这是指String类型。下面有一个经典的String对象生成的面试题!

4、java堆是JVM中内存最大的一块,被所有线程共享。几乎所有的对象实例都在这里分配,所以java堆也是JVM垃圾回收的主要区域。java堆又被分成了年轻代,老年代;年轻代进一步可以划分为Eden空间,From Survivor空间、To Survivor空间。

当我们使用new关键字分配对象时,就是在java堆中生成对象。

下面分析一下对象生成时的情况。

因为Eden最大,所以新生成的对象都分配到Eden空间,当Eden空间快满时,进行一次Minor GC,然后将存活的对象复制到From Survivor空间。这时,Eden空间继续向外提供堆内存。

后面继续生成的对象还是放到Eden空间,当Eden空间又要满的时候,这时候Eden空间和From Survivor空间同时进行一次Minor GC,然后把存活对象放到To Survivor空间。这时,Eden空间继续向外提供堆内存。

接下来的情况和2一致。Eden空间快满的时候,Eden空间和To Survivor空间进行一次Minor GC,然后存活的对象放到From Survivor空间。

接下来的情况和3一致。Eden空间快慢的时候,Eden空间和From Survivor空间进行一次Minor GC,然后存活的对象放到To Survivor空间。

就是说2个Survivor中的一个用来提供对象保存。当Eden空间和某一块Survivor空间GC后,另一块Survivor空间放不下GC后存活的对象;或者是连续Minor GC15次左右的情况;就把这部分存活对象放入到老年代空间。

当老年代空间也放满的时候,进行Major GC,对老年代空间进行回收。(也叫做Full GC,Full GC的内存消耗很大,应该避免)

年轻代使用的是复制算法:每次Minor GC把Eden区和一块Survivor区的存活对象复制到另一块Survivor区。老年代使用的是标记-整理算法:每次Major GC把存活对象都想内存空间的一端移动,然后直接清理掉端边界以外的内存。

大对象如数组、很长的字符串,直接进入老年代空间。

5、方法区用于存储JVM加载的类信息、final常量、static静态变量等数据,方法区中的数据都是整个程序中唯一的。方法区还包含了运行时常量池,主要存放编译期生成的字面量和符号引用(在类加载后放入)。String对象的字面量就会被放入到运行时常量池中。

垃圾回收在方法区主要是对常量的回收和对类型的卸载。

2、jdk1.8及之后的jvm内存区域,元空间取代了永久代

参考文章:java8的jvm内存区域。

元空间和永久代的性质是一样的,都是对JVM方法区的实现,作用是一样的。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机JVM内存中,而是使用本地内存。

为什么用元空间取代永久代呢?

字符串存在永久代中,容易出现性能问题和内存溢出。

类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。

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

直接内存

JDK1.4之后加入的NIO,引入了基于通道channel和缓冲区buffer的IO,直接使用native函数分配堆外内存,显著提高IO性能,避免了原来BIO的在java堆和naive堆中来回复制数据。

3、字符串String生成时的内存分配情况

参考文章:字符串常量池。

4、生成对象时的内存情况

下面来分析一下我们常见的生成对象或基本数据类型变量的内存模型。这样可以对JVM有一个更好的理解。

int i =3;,一个方法对应一个栈帧,方法中的基本数据类型变量直接在栈帧中分配。如果是static、final类型的基本数据类型则存储在运行时常量池中,和String一样

Object o1 = new Object();,对象引用(Object o1)存储在栈帧中,但是对象数据(new Object())存储在java堆中,对象类型数据(Class等信息)存储在方法区中。

String s1 = new String("abcd");,使用new声明的对象,对象引用(String s1)存储在栈帧中,对象数据(new String(“abcd”))存储在java堆中,字符串值(“abcd”)存储在运行时常量池中。

String s2 = “abc”,对象引用(String s2)存储在栈帧中,字符串值(“abc”)存储在运行时常量池中。

java栈、java堆、方法区这3者之间的关系大概就是上面的分析所示。

3、各种异常分析 1、java堆内存溢出错误OutOfMemoryError

如果java堆中分配的对象太多,且GC后内存空间还是不够用。下面通过循环生成对象来消耗内存空间进行测试。

相关指令:VM Args: -Xms20m -Xmx40m,表示JVM分配的堆内存最小为20MB,最大为40MB。

 public static void main(String[] args) {
   while (true) {
     List list = new ArrayList<>(10);
     list.add(new Object());
   }
 }
2、java栈堆栈溢出错误StackoverflowError

如果java栈的栈深度大于JVM允许的深度,就会抛出该错误。下面通过无限递归调用来进行堆栈进行测试。

相关指令:VM Args: -Xss128k,表示JVM分配的栈容量为128KB。

public class StackOOM {
    
    private int length = 1;
    
    public void stackLeak() {
        length++;
        stackLeak();
    }
    
    public static void main(String[] args) {
        StackOOM stackOOM = new StackOOM();
        stackOOM.stackLeak();
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71684.html

相关文章

  • JVM内存模型

    摘要:的内存模型概述虚拟机在执行程序的过程中,会把它所管理的内存划分为若干个不同的数据区域。程序计数器这是一块较小的内存,它可以看做是当前线程所执行的字节码的行号指示器。 JVM的内存模型 概述 Java虚拟机在执行java程序的过程中,会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则依赖用户线程的启动...

    andycall 评论0 收藏0
  • JVM运行数据区域

    摘要:虚拟机在执行程序的过程中会把它所管理的内存划分为若干个不同的数据区域,本篇文章将会对这些数据区域进行简略的介绍。运行时常量池运行时常量池是方法区的一部分。直接内存直接内存不是虚拟机运行时数据区的一部分,不是虚拟机规范中定义的内存区域。 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,本篇文章将会对这些数据区域进行简略的介绍。JVM所管理的内存包括的...

    meislzhua 评论0 收藏0
  • JVM 内存模型

    摘要:是描述方法执行的内存模型每个方法执行的时候会同时创建一个栈帧,用于存储局部变量表操作数栈动态连接返回地址方法出口等信息。虚拟机是使用局部变量表完成参数值到参数变量表的传递过程。堆内存管理最大的一块。 showImg(https://segmentfault.com/img/bVLqsv?w=475&h=398); 1. 虚拟机栈 VM Stack 线程私有,生命周期与线程相同。VM S...

    SexySix 评论0 收藏0
  • JVM 的 工作原理,层次结构 以及 GC工作原理

    摘要:执行引擎作用执行字节码,或者执行本地方法运行时数据区其实就是指在运行期间,其对内存空间的划分和分配。 虽是读书笔记,但是如转载请注明出处https://uestc-dpz.github.io..拒绝伸手复制党 JVM Java 虚拟机 Java 虚拟机(Java virtual machine,JVM)是运行 Java 程序必不可少的机制。JVM实现了Java语言最重要的特征:即平台...

    qqlcbb 评论0 收藏0
  • JVM 探究(一):JVM内存模型概念模型

    摘要:作为一个程序员,不了解内存模型就不能写出能够充分利用内存的代码。程序计数器是在电脑处理器中的一个寄存器,用来指示电脑下一步要运行的指令序列。在虚拟机中,本地方法栈和虚拟机栈是共用同一块内存的,不做具体区分。 作为一个 Java 程序员,不了解 Java 内存模型就不能写出能够充分利用内存的代码。本文通过对 Java 内存模型的介绍,让读者能够了解 Java 的内存的分配情况,适合 Ja...

    cnTomato 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<