首页 > 其他分享 >JVM 运行时数据区域介绍

JVM 运行时数据区域介绍

时间:2023-06-06 13:01:51浏览次数:37  
标签:Java 虚拟机 回收 区域 线程 内存 JVM 方法 运行


Java的动态内存分配和垃圾回收机制使java程序员不用像C++程序员那么头疼内存的分配与回收。相信熟悉COM机制的朋友对于引用计数管理内存的方式深有感触。

Java虚拟机的自动内存管理不仅降低了编码的难度而且不容易出现内存泄露和内存溢出的问题。但是这过于美好的愿景正是由于把内存的控制权交给了Java虚拟机,一旦出现内存泄露和溢出,我们就必须翻过Java虚拟机自动内存管理这堵高墙去排查错误。本文简要总结下JVM运行时数据区域的划分、作用以及可能出现的异常。

图1 Java虚拟机运行时数据区

如图1所示,根据《Java虚拟机规范(Java SE 7 Edition)》的规定,Java虚拟机在执行Java程序时,即运行时环境下会把其所管理的内存划分为几个不同的数据区域。有的区域伴随虚拟机进程的启动而创建,死亡而销毁;有些区域则是依赖用户线程的启动时创建,结束时销毁。所有线程共享方法区和堆,虚拟机栈、本地方法栈和程序计数器是线程隔离的数据区。

1.1程序计数器Program Counter Register

由于操作系统通过时间片轮流的多线程并发方式,任何时刻处理器只会处理当前线程的指令。线程间切换的并发要求每个线程都需要有一个私有的程序计数器,程序计数器间互不影响。

程序计数器存储当前线程下一条要执行的字节码的地址,占用内存空间较小。所有的控制执行流程,分支、循环、返回、异常等功能都在程序计数器的指示范围之内,字节码解释器通过改变程序计数器的值来获取下一条要执行的字节码的指令。

1.2 虚拟机栈 VM Stack

简单点说,虚拟机栈就是类中的方法的执行过程的内存模型。虚拟机栈也是线程私有的,并且同线程的生命周期相同。

对于方法的调用,有必要先介绍下栈帧(Stack Frame)。

虚拟机在执行每个方法的调用时会创建一个栈帧的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。每个方法的调用过程,就对应着一个栈帧在虚拟机里的入栈出栈的过程。栈帧包括了方法的局部变量表、操作数栈、动态链接和方法出口等一些额外的附加信息。对于活动线程中栈顶的帧栈,称为当前栈帧,这个栈帧所关联的方法称为当前方法,正在执行的字节码指令都只针对当前有效栈帧进行操作。

在栈帧的基础上,不难理解虚拟机栈的内存结构。Java虚拟机规范规定虚拟机栈的大小是可以固定的或者动态分配大小。Java虚拟机实现可以向程序员提供对Java栈的初始大小的控制,以及在动态扩展或者收缩Java栈的情况下,控制Java栈的最大值和最小值。

以下异常情况与Java栈相关:

  • 如果线程请求的栈深度大于虚拟机所允许的深度,则Java虚拟机将抛出StackOverflowError异常。
  • 如果虚拟机栈可以动态扩展,但是无法申请到足够的内存来实现扩展,或者不能得到足够的内存为一个新线程创建初始Java栈,则Java虚拟机将抛出OutOfMemoryError异常。

1.3本地方法栈 Native Method Stack

本地方法栈内执行的是非Java语言编写的代码,比如C或C++,而虚拟机栈执行的是java方法字节码服务,这是两者最大的区别。本地方法栈的是虚拟机使用本地方法服务的,如果提供本地方法栈,则它们通常在每个线程被创建时分配在每个线程基础上的。

同虚拟机栈一样,本地方法栈也会出现与虚拟机栈类似的异常,也会抛出StackOverflowError和OutOfMemoryError异常。

1.4 Java堆 Java Heap

Java堆是类实例和数组的分配空间,是一块所有线程共享的内存区域。堆在虚拟机启动时创建,是Java虚拟机所管理的内存中最大一块。内存泄露和溢出的问题大都出现在堆区域,由此,Java堆是垃圾回收的主要重点管理区。

从内存回收的角度看,由于现在收集器基本上都是采用的分代收集算法,Java堆还可细分为新生代和老年代;从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓存区。这种进一步的内存划分方式目的是更好地回收内存,或者更快地分配内存。

Java虚拟机规范规定堆在内存单元中只要在逻辑上是连续的即可,Java堆可以是固定大小的,或者按照需求做动态扩展,并且可以在一个大的堆变的不必要时收缩。Java虚拟机的实现向程序员或者用户提供了对堆初始化大小的控制,以及对堆动态扩展和收缩的最大值和最小值的控制。

以下异常情况与Java堆相关:

  • 如果堆中没有可用内存完成类实例或者数组的分配,在对象数量达到最大堆的容量限制后将抛出OutOfMemoryError异常。

1.5 方法区 Mehod Area

方法区在虚拟机启动时创建,也是一块所有线程共享的内存区域。方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。用一句话说就是方法区类似于传统语言的编译后代码的存储区。

虽然Java虚拟机规范在逻辑上把方法区描述为堆的一个部分,但是在垃圾回收方面的限制却比较宽松,宽松到方法区可以不用实现垃圾回收。但是,垃圾回收在方法区还是必须有的,只是回收效果不是很明显。这个区域的回收目标主要针对的是常量池的回收和对类型的卸载。

方法去的大小也可控制,以下异常与方法区相关:

  • 如果方法区无法满足内存分配需求时,将会抛出OutOfMemoryError异常。

1.6运行时常量池 Runtime Constant Pool

常量池是每个类的Class文件中存储编译期生成的各种字面量和符号引用的运行期表示,其数据结构是一种由无符号数和表组长的类似于C语言结构体的伪结构,详细内容请参考《Java虚拟机规范第七版》第四章。

常量池也是方法区的一部分,类的常量池在该类的Java class文件被java虚拟机成功地装载时创建,这部分内容在类加载后存放到方法区的运行时常量池中。

运行时常量池属于方法区,自然也受到方法区内存大小的限制,以下异常与常量池有关:

  • 在装载class文件时,如果常量池的创建需要比Java虚拟机的方法区中需求更多的内存时,将会抛出OutOfMemoryError异常。

通过总结,对于虚拟机运行时数据区域的划分及每个区域作用,存储内容及可能出现的异常有了一个大致的了解。Java的自动内存分配和垃圾回收筑起的这道高墙,在出现内存泄漏或者溢出的情况下,这道高墙就必须翻越了。

标签:Java,虚拟机,回收,区域,线程,内存,JVM,方法,运行
From: https://blog.51cto.com/u_2650279/6424044

相关文章

  • 如何在运行并调试React Native App
     1.进入工程目录,启动:Metro  npxreact-nativestart 2.然后在新的terminal窗口中运行app   npxreact-nativerun-ios第1,2步参考:https://reactnative.dev/docs/environment-setup#running-your-react-native-application这时你的程序应该运行起来了。每次操作可......
  • 阿里云 arthas JVM 诊断工具常用命令记录
    看完快速入门再回来:https://arthas.aliyun.com/doc/quick-start.htmljad:https://arthas.aliyun.com/doc/jad.html反编译class文件,查看JVM加载的class文件源代码,类名后面跟一个空格加方法名可以单独反编译某一个方法源代码jadcom.geostar.geoonline.service_visit_log.d......
  • 用c实现:编译出来的可执行文件运行完成后删除自己
    没办法。可执行文件和bat脚本文件不一样,它运行时会被加锁,无法获取写权限。原由:因为每次编译一个c文件后都会生成一个exe文件,占用了我的视野资源,所以我每次都要定期运行一个批处理脚本来清理这些文件。于是我就想,exe文件自己不是也有运行权限吗,它能不能自己获取自己的写权限在执行......
  • 4月7日严老师JVM面试资料
    JVM金三银四面试突击班21.JVM常用的参数有哪些?讲师:严镇涛标准参数-version-help-server-cp 3.1.2-X参数非标准参数,也就是在JDK各个版本中可能会变动-Xint    解释执行-Xcomp   第一次使用就编译成本地代码-Xmixed  混合模式,JVM自己来决定 3.1.3-XX参数......
  • docker 查看启动容器的运行命令 参数
    1.dockerps-a--no-truncCONTAINERIDIMAGECOMMAND......
  • JVM专栏-类加载的过程
    类加载的过程类加载过程包括5个阶段:加载、验证、准备、解析和初始化。加载加载的过程“加载”是“类加载”过程的一个阶段,不能混淆这两个名词。在加载阶段,虚拟机需要完成3件事:通过类的全限定名获取该类的二进制字节流。将二进制字节流所代表的静态结构转化为方法区的......
  • c++实现射线法 点和闭合区域位置关系判断
    c++实现射线法  点和闭合区域位置关系判断#include<iostream>#include<vector>structPoint{doublex;doubley;};structPolygon{std::vector<Point>vertices;};//定义三个点的方向//0-->点p,q,r是共线的//1-->顺时针//2-->......
  • java 图形学 点和闭合区域位置关系判断
    判断一个点是否在一个特定的区域内或外,通常需要你具备区域的数学表达(例如,方程、不等式等)以及要判断的点的坐标。例如,如果你有一个圆的方程(x-h)²+(y-k)²=r²,其中(h,k)是圆心,r是半径,你要判断点(a,b)是否在此圆内或外。你可以将点(a,b)的坐标带入这个方程:如果(a......
  • Docker运行Django框架
    Django框架创建django-pg项目目录[root@docker~]#mkdirdocker-compose-django[root@docker~]#cddocker-compose-django/[root@dockerdocker-compose-django]#mkdirdjango-pg在项目目录下创建docker-compose.yml文件该文件定义了两个服务,一个是名为db的Postgres数......
  • JVM 诊断神器-Arthas实战
    什么是Arthas(阿尔萨斯)阿里开源的Java诊断工具,它可以在运行时对Java应用程序进行动态诊断和调试当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决这个类从哪个jar包加载的?为什么会报各种类相关的Exception?我改的代码为什么没有执行到?难道是我没commit?分支搞错......