JVM内存模型_gvm内存模型-程序员宅基地

技术标签: JVM  java  JVM内存模型  深入理解JVM  

Java在诞生之初就提出了"Write once,Run Anywhere"的口号,而这些都得益于JVM(Java Vritual Machine),可以提前在不同的运行环境(linux或windows等)上安装JDK之后,就可以让同一份代码在任何地方运行了。而这里的JDK(Java Development Kit)是Java语言、Java虚拟机和Java类库的统称。

一、JDK体系结构与Java跨平台特性

1.1 JDK体系结构

JDK是一个非常庞大的体系,里面包含了JVM、Java SE API(核心类库)、常用工具(javac等)以及Java语言等等一系列内容,其具体的体系结构图如下所示:

1.2 Java跨平台特性

Java的跨语言特性在文章开始已经提过了,通过在不同系统安装对应的JDK(主要是JVM)版本就可以实现,大概如下图所示:

二、JVM整体结构与内存模型

2.1 JVM整体结构

JVM称为Java虚拟机,作为一个虚拟机它首要的功能就是对应用程序的线程等分配内存空间,而JVM还要负责加载类、解析执行编译后的字节码文件,所以整个JVM主要就包含了运行时数据区、类加载子系统和字节码执行引擎。

大概的结构如下图所示:

在Java虚拟机外部,还有一些本地方法接口,这些接口也是JVM经常需要调用。

2.2 内存模型

Java虚拟机的运行时数据区显然是在内存中的,所以这一部分也成为JVM的内存模型,这里要与JMM(Java内存模型)区分开。

内存模型主要包含五部分的内容:堆、栈、本地方法栈、方法区(元空间)、程序计数器。

:JVM管理的最大一块内存空间,它是所有线程所共享的一块区域。在虚拟机启动的时候创建,该区域的唯一目的就是为了存放对象实例,几乎所有通过new创建的实例对象都会被分配在该区域。

栈(虚拟机栈):也可以称为虚拟机线程栈,它是JVM中每个线程所私有的一块空间,每个线程都会有这么一块空间。它的生命周期是与线程的生命周期是绑定的。虚拟机栈描述了Java中方法执行时的内存模型,即每个方法被执行的时候,线程都会在自己的线程栈中同步创建一个栈帧(Stack Frame),用于存放局部变量表、操作数栈、动态连接和方法出口等信息,每个方法从调用到完成的过程,就对应着一个栈帧在线程栈中从入栈到出栈的过程。

本地方法栈:本地方法栈与虚拟机栈的作用是相似,不同的是虚拟机栈为JVM执行的Java方法服务,而本地方法栈为JVM调用的本地方法服务。HotSpot虚拟机直接把本地方法栈和虚拟机栈合二为一。

程序计数器:只需要占用一小块的内存空间,每个线程都会有自己独立的程序计数器,主要功能就是记录当前线程执行到哪一行指令了,可以看作是当前线程所执行的字节码行号指示器。

方法区(元空间):在JDK 8之前,方法区也称之为永久代,这部分区域与堆一样,是所有线程所共享的,它主要用于存放被虚拟机加载的类型信息、常量、静态变量以及即时编译器编译后的代码缓存等数据。对于一个Class文件,除了版本、字段、方法、接口等描述信息外,还有常量池表,在上一篇文章《JVM类加载机制》中提到过,主要用于编译器生成的各种字面量和符号引用,而这部分内容在Class文件加载后是存放在方法区的运行时常量池中。这个运行时常量池自然还包括了字符串常量池,但需要注意的是,在JDK 7以后的版本中,字符串常量池和静态变量等被移至到了Java堆区,而到了JDK 8,抛弃了之前永久代的概念,通过在本地内存中实现了元空间(Meta-space)来代替永久代,并把JDK 7中永久代剩余内容(主要是类型信息)全部移至到了元空间

所以,方法区是使用直接内存来实现,这与堆是不一样的,也就是堆和方法区用的并不是同一块物理内存。

直接内存:直接内存并不是JVM运行时数据区的一部分,其分配不会受Java堆大小的限制。

下面以一个列子来演示JVM内存模型的各个部分,创建一个测试类如下:

public class Test {
    

    public int compute(){
    
        int a = 4;
        int b = 5;
        int c = a * b * 6;
        return c;
    }

    public static void main(String[] args) {
    
        Test test = new Test();
        int compute = test.compute();
        System.out.println(compute);
    }
}

将Test类编译之后,然后在控制台通过javap -c Test.class命令输出Class文件对应的字节码指令,下面主要分析compute()方法的字节码指令,结合JVM的内存模型。

public int compute();
	Code:
        0: iconst_4
        1: istore_1
        2: iconst_5
        3: istore_2
        4: iload_1
        5: iload_2
        6: imul
        7: bipush        6
        9: imul

下面是执行Test的main方法时,线程栈的模型,下面结合下面的模型和字节码指令来介绍运行时数据区的各个模块。
在这里插入图片描述

当执行Test类的main方法时,首先会将Test.class文件加载进JVM的方法区,主要有常量池和方法定义,而常量池中此时包含一个compute()的符号引用,而在执行main方法,当调用compute()方法时,会将compute()的符号引用变成该方法具体在方法区的内存地址(直接引用),因为这个过程是在程序运行时发生的,所以称为动态链接

在执行main()compute()方法时会分别在main线程栈中创建两个栈帧,因为main()先调用,所以其栈帧处于栈的底部。

通过compute()内存地址找到该方法包含的字节码指令就可以执行compute()方法了。

1、iconst_4表示将int类型数值4压入操作数栈中,此时compute()栈帧中的操作数栈中就只有一个数字4

2、istore_1表示将操作数栈顶的int类型数值放入到局部变量表的第二个槽位中(出栈),此时操作数栈就没有元素了。

注:局部变量表中,是以槽位为基本单位来存储数值,第一个槽位的索引下标为0,且存放的局部变量为当this引用

3、iconst_5表示将int类型数值5压入操作数栈中

4、istore_2表示将操作数栈顶的int类型数值放入到局部变量表的第三个槽位中

5、iload_1表示将局部变量表的第二个槽位中int类型的数值放入到操作数栈的栈顶

6、iload_2表示将局部变量表的第三个槽位中int类型的数值放入到操作数栈的栈顶

7、imul表示将操作数栈的元素全部出栈交给CPU进行乘法运算,然后再将运算结果压入操作数栈的栈顶

第七步完成后,此时操作数栈里面的数值只有一个20

8、bipush 6表示将单字节常量6放入到操作数栈的栈顶

9、imul将操作数栈中的int类型的元素20和6出栈交给CPU进行乘法运算,然后将计算结果120压入操作数栈的栈顶

10、istore_3表示将操作数栈顶的int类型数值放入到局部变量表的第四个槽位中

11、iload_3表示将局部变量表的第四个槽位中int类型的数值放入到操作数栈的栈顶

12、ireturn表示从当前方法返回int类型的数值,即操作数栈中的数值。

至此compute()方法就执行完成了,执行完之后,需要知道将返回到哪里去继续执行,这就是方法出口的作用,它会记录方法执行完之后,跳转到哪里。

注:对于这些常用的字节码指令可以参考周志明的《深入理解Java虚拟机》

三、JVM内存参数

JVM内存模型中的这些区域,都是有大小限制的,当然也可以通过JVM提供的参数来设置这些区域所占内存的大小。

补充:在JVM堆中的实例对象,会按照它们的年龄或大小分为新生代或老年代,而新生代的内存空间又分为Eden区和Survivor区,如下图所示:

至于为什么要这样划分内存空间,会在下篇文章介绍JVM的垃圾收集机制时详细介绍。

下面介绍这些常用参数的含义:

-Xss:表示每个线程栈的大小

-Xms:表示堆空间的初始可用大小,默认为物理内存的1/64

-Xmx:表示堆空间的最大可以大小,默认为物理内存的1/4(这个参数在应用程序中是一定要指定的)

-Xmn:表示新生代(年轻代)的大小

-XX:NewRatio:默认为2,表示新生代占年老代的1/2,占整个堆内存的1/3。

-XX:SurvivorRatio:默认为8,表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。

-XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。

-XX:MetaspaceSize: 指定元空间触发Full Gc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M左右,达到该值就会触发full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。

由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般推荐将这两个值都设置为256M。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sermonlizhi/article/details/124774141

智能推荐

nginx 命令 启动 停止_ngix 开启结束命令-程序员宅基地

文章浏览阅读399次。启动操作命令:nginx -c /usr/nginx/conf/nginx.conf-c参数指定了要加载的nginx配置文件 路径。停止操作停止操作是通过向nginx进程发送信号(什么是信号请参阅linux文章)来进行的步骤1:查询nginx主进程号ps -ef | grep nginx在进程列表里 面找master进程,它的编号就是主进程号了。步骤2:发送_ngix 开启结束命令

Qlik十月版本带你体验最新的外观主题!_qlik在线体验-程序员宅基地

文章浏览阅读169次。在十月份发布的版本中,Qlik对用户自定义其云分析体验带来了新的可能性。新的图表监控增强功能和对集合的更轻松访问使用户可以直接从云中心轻松组织和访问其最关键的见解和可视化。全新的默认主题提供了更好,外观更现代的应用程序。此版本还包括通过Qlik DataTransfer扩展的数据连接选项,数据重新加载管理以及具有JWT授权的新身份管理配置选项。自定义您的中心和更快地访问关键见解图表监控增强用户拥有更多选择,可以直接从云中心监视最重要的图表。现在,除了用户创建的图表外,用户还可以选择由Insigh_qlik在线体验

java.lang.NoClassDefFoundError: org/apache/tez/dag/api/SessionNotRunning-程序员宅基地

文章浏览阅读7k次,点赞7次,收藏4次。IDEA编写程序:spark SQL连接 hive报错:java.lang.NoClassDefFoundError: org/apache/tez/dag/api/SessionNotRunning我的问题出现在:方法一:把hive-site.xml拷贝到idea的resources中后,hive-site.xml中的一个配置需要改:简单来说,把 “hive.execution.eng..._java.lang.noclassdeffounderror: org/apache/tez/dag/api/sessionnotrunning

Unity场景切换(同步切换,异步切换)_unity使用事件侦听器切换场景-程序员宅基地

文章浏览阅读7.3k次,点赞8次,收藏34次。Unity场景切换(同步切换,异步切换)在游戏中终会会有场景的切换,因此在我们做完场景UI后或者是在对游戏完成后,场景的切换(从选关菜单进入到游戏界面,反之亦然)都会用到场景切换SceneManager.LoadScene();同步加载,用于切换场景(非异步)例如在我们需要去从选关菜单进入到游戏界面时既可以这个方法,在此方法的参数中,需要加入"String",String为所需加载场景的名称。当然在此之前我们需要把两个场景放入一个 Building Setting中(在File下,也可以通过快捷键_unity使用事件侦听器切换场景

路由器的应用场所及作用_2.分析路由器的作用及适用场合?-程序员宅基地

文章浏览阅读4.8k次,点赞4次,收藏10次。路由器、交换机的应用场所及作用路由器的应用场所路由器的作用:路由器的主要功能:路由器的应用场所路由器是连接两个或多个网络的硬件设备,在网络间起网关的作用,是读取每一个数据包中的地址然后决定如何传送的专用智能性的网络设备。它能够理解不同的协议,例如某个局域网使用的以太网协议,因特网使用的TCP/IP协议。这样,路由器可以分析各种不同类型网络传来的数据包的目的地址,把非TCP/IP网络的地址转换成TCP/IP地址,或者反之;再根据选定的路由算法把各数据包按最佳路线传送到指定位置。所以路由器可以把非TCP/ _2.分析路由器的作用及适用场合?

Machine Learning - XV. Anomaly Detection异常检测 (Week 9)_machine learning for anomaly detection: a systemat-程序员宅基地

文章浏览阅读5.6k次,点赞2次,收藏4次。机器学习Machine Learning - Andrew NG courses学习笔记Anomaly Detection异常检测Problem Motivation问题的动机Gaussian Distribution高斯分布Algorithm算法Developing and Evaluating an Anomaly Detection S_machine learning for anomaly detection: a systematic review

随便推点

用inkscape临摹logo_inkscape 画平行四边形-程序员宅基地

文章浏览阅读470次。《用inlscape临摹太极图logo》_inkscape 画平行四边形

实现哈希表的相关运算算法_建立关键字序列(16,74,60,43,54,90,46,31,29,88,77)对应的哈希表a[0-程序员宅基地

文章浏览阅读5.4k次,点赞4次,收藏52次。/*** 实验题目:* 实现哈希表的相关运算算法* 实验目的:* 领会哈希表的构造和查找过程及其相关算法设计* 实验内容:* 设计程序,实现哈希表的相关运算,并完成如下功能:* 1、建立关键字序列(16, 74, 60, 43, 54, 90, 46, 31, 29, 88, 77)对应的哈希* 表A[0..1..._建立关键字序列(16,74,60,43,54,90,46,31,29,88,77)对应的哈希表a[0..12],哈希函

部署Kubernetes kube-apiserver启动失败_kube-apiserver.service holdoff time over, scheduli-程序员宅基地

文章浏览阅读3.4w次,点赞2次,收藏6次。systemctl restart kube-apiserver启动失败[root@centos-master yum.repos.d]# systemctl status kube-apiserver.service● kube-apiserver.service - Kubernetes API Server Loaded: loaded (/usr/lib/systemd/syst..._kube-apiserver.service holdoff time over, scheduling restart.

Matlab 生成license_matlab 2023试用期license-程序员宅基地

文章浏览阅读845次,点赞8次,收藏5次。Matlab 服务器端激活_matlab 2023试用期license

CodeSign error: code signing is required for product type Application in SDK iOS-程序员宅基地

文章浏览阅读53次。原地址:CodeSign error: code signing is required for product type Application in SDK iOS 在真机测试的时候往往会突然出现这样一个错误,code signing is required for product type 'Application' in SDK 'iOS 7.0' ,就是说代码签名证书不对劲。..._hellocordova doesn't have a bundle identifier. add a value for product_bundl

【PTA】线性表的两个非递减集合求并集_数据结构线性表的两个非递减集合求并集pta-程序员宅基地

文章浏览阅读4.1k次,点赞6次,收藏7次。线性表的两个非递减集合求并集(山东大学威海校区大二数据结构实验)_数据结构线性表的两个非递减集合求并集pta

推荐文章

热门文章

相关标签