2.JVM内存结构

2.JVM内存结构

一、JVM字节码执行引擎、类装载子系统、运行时数据区组成。

1.类装载子系统主要是把class文件加载到运行时数据区中,

2.字节码执行引擎主要是执行字节码文件,并把执行的相应的行数数据推送到运行时数据区中的程序计数器中

3.运行时数据区是文件执行的主要场所。

二、运行时数据区由下面几个区域构成:

1.堆:大多数对象创建之后都放在堆中,少数放在栈中

2.栈:每个线程产生都会有一个独有的线程栈(在栈上分的一小块内存),栈的主要作用是为程序运行提供场所,即程序的执行操作在栈中进行

3.本地方法栈:存放一些执行本地方法的地址指针,主要是一些早期的c++执行的本地方法

4.程序计数器:记录程序运行的位置,如果被打断之后可以接着打断的地方接着运行

5.方法区(元空间):存放一些静态变量、类元信息等

元空间和堆是所有执行文件所共享的区域,即一个静态变量被创建之后可以被另一个线程所引用

而每一个线程都有自己的其他区域,即线程栈、程序计数器和本地方法栈,线程结束后即被销毁

三、分别介绍:

1.堆(垃圾回收机制):堆分为年轻代和老年代,空间大小为1:2,年轻代又分为eden区和survivor区,比例为8:1:1,以上两个比例皆可以修改

对象在被创建后放入Eden区中,当Eden区放满之后,会进行一次minor gc,通过可达性算法分析,将垃圾对象进行回收,然后将不是垃圾的对象放入survivor1区中,经过程序执行之后,如果eden区再次放满,则会将Eden区和survivor1区中的对象进行垃圾回收,将仍然存活的对象放入survivor2区中,即每次进行垃圾回收后的对象都放在另一个survivor区中,直到对象的分带年龄达到设定值之后(默认为15,因为对象中的分代年龄指针占四个字节,最大值为15),放入老年代,其中,对象每经历一次minor gc,分代年龄就会加一。老年代经过多次minor gc后,也会放满,如果老年代放满,程序会进行full gc,即,程序会进行STW(stop the ),即终止程序运行,然后通过可达性分析算法,进行一次full gc。如果对象仍然存活,则继续存放在老年代中,如果老年代的对象full gc之后仍然满了,则会出现内存溢出的错误情况。

2.栈:

栈是程序运行的主要场所,每有一个线程运行时,jvm就会给这个线程在栈上分配一块独立的区域(线程栈)去存放自己的局部变量,线程栈的主要由栈帧组成,每个方法都有一个独立的栈帧,栈帧上主要有:方法出口、动态链接、操作数栈、局部变量表组成。比如执行main方法,则main方法有自己独立的栈和本地方法栈以及程序计数器,根据程序计数器,来进行记录执行的位置,通过字节码执行引擎进行执行,执行main方法时,会给main方法生成一个独立的栈帧,栈帧中本地变量区域放入方法中生成的局部变量,对局部变量进行操作时,将局部变量放入操作数栈中(局部变量做操作时用来中转存放的内存空间),当执行操作时,将操作数栈中的数据顺序出栈,放入外部寄存器中,用运算器进行运算,运算出来结果之后再把值放入操作数栈中,再进行赋值给本地变量。动态链接:程序在加载时,在解析阶段只会进行静态链接(将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用,下节课会讲到动态链接),即只解析静态方法,当程序运行到静态方法中的普通方法时,会进行动态链接。因为程序在加载阶段会把编译为一条条指令文件,然后存放在元空间的常量池中,所以在执行动态链接时,便是找到该方法的在元空间常量池的地址。方法出口:一个方法运行完成之后返回到上层调用方法的哪一个位置。如果方法中引用对象信息,则对应点局部变量表中保存的是堆中对象的地址指针。

3.程序计数器:

记录程序运行的位置,如果程序暂停,再重新开始后,程序会从程序计数器的位置开始执行,程序计数器的位置是外部字节码执行引擎执行发送的地址。

4.本地方法栈:

存放一些本地底层的方法。

5.方法区(元空间):

存放静态变量、字符串常量池、对象常量池、类元信息等。静态变量:文件在加载过程中,在准备阶段对一些静态变量进行分配空间,并赋予默认值,在初始化阶段再进行赋初始值。常量池:存放相应信息。类元信息:文件在加载过程中,在加载阶段,文件会编译为字节码文件,其中,相应的类中的方法、变量等相应的信息为类元信息。静态变量是对象类型,则存放指向该对象在堆中的地址。

四、相互之间的联系:

通过类装载子系统进行装载类,然后通过字节码执行引擎进行执行,执行main()方法时,在栈中划分一小部分空间生成线程栈,在线程栈中为main方法生成独立栈帧,如果类中有其他方法,则生成一个新的栈帧,在栈帧中的局部变量表中存放执行过程中的局部变量,在操作数栈中进行对操作数进栈以及出栈,然后在外部执行运算,在赋值给本地变量,其中执行步骤都是由该线程独有的程序计数器计数执行位置供字节码执行引擎执行。如果栈帧中局部变量为对象类型,则局部变量表中存放的是堆中对象的指针地址。该对象头中存在指针,指向其所属的类,也就是指向方法区中该类的地址。当一个类加载到jvm中会产生类对象,即在堆中存放的class对象,方法区中的类元信息会再次指向堆中的class类对象。

如果方法区(元空间)中静态变量是对象类型,则方法区(元空间)的常量池中存放指向该对象在堆中的地址。

原文 

https://segmentfault.com/a/1190000023248578

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 2.JVM内存结构

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址