首页 > 系统相关 >Java虚拟机之自动内存管理

Java虚拟机之自动内存管理

时间:2023-05-03 15:56:06浏览次数:50  
标签:Java 常量 虚拟机 线程 内存 方法

1 内存区域

1.1 运行时数据区域

运行时数据区域可以划分为由所有线程共享的方法区、堆和线程隔离的虚拟机栈、本地方法栈、程序计数器。

1.1.1 程序计数器(Program Counter Register)-线程隔离

程序计数器是一块较小的内存空间,它是当前线程所执行的字节码的行号指示器

Java虚拟机的多线程是通过多线程轮流切换、分配处理器执行时间的方式来实现的。为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地方法(Native),这个计数器值则应为空。

该内存区域是唯一一个不存在OutOfMemoryError情况的区域

1.1.2 Java虚拟机栈(Java Virtual Machine Stack)-线程隔离

Java虚拟机栈是线程私有的,它的生命周期与线程相同。每个Java方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

局部变量表存放了编译期Java虚拟机基本数据类型、对象引用和returnAddress类型(指向了一条字节码指令的地址)
局部变量表中的存储空间以局部变量槽(Slot)来表示,long和double类型的数据占用两个变量槽,其余数据类型占用一个。
局部变量表所需内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间时完全确定的,方法运行期间不会改变局部变量表的大小(即变量槽的数量)

如果线程请求的栈深大于虚拟机所允许的深度,将抛出StackOverflowError;如果Java虚拟机栈容量可以动态扩展,当扩展时无法申请到足够的内存,会抛出OutOfMemoryError

1.1.3 本地方法栈(Native Method Stacks)-线程隔离

虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用本地(Native)方法服务。

与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError异常和OutOfMemoryError异常

1.1.4 堆(Java Heap)-线程共享

Java堆是虚拟机所管理的内存中最大的一块,Java堆是被所有线程所共享的一块内存区域,在虚拟机启动时创建。

The heap is the runtime data area from which memory for all class instances and arrays is allocated.

所有的对象实例以及数组都应当在堆上分配

  • 《Java虚拟机规范》

Java堆的作用是存放对象实例几乎所有的对象实例都在这里分配内存。随着即时编译技术的发展,Java对象实例分配在堆上也变得不是那么绝对了。

Java堆也称GC堆,是垃圾收集器管理的区域。大部分垃圾收集器都是基于分代收集理论设计的,所以往往我们会将Java堆划分为新生代、老年代等不同区域,以便更好地回收内存。
虽然Java堆是线程共享的,但是我们也可以为各个线程划分出私有的分配缓冲区,以便更快地分配堆内存

Java堆既可以被实现成固定大小的,也可以是可扩展的(一般是可扩展的,通过-Xms和-Xmx设定堆大小)。当Java堆内存不足以完成实例分配时,将会抛出OutOfMemoryError

1.1.5 方法区(Method Area)-线程共享

方法区也是各线程共享的内存区域,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。《Java虚拟机规范》将此区域描述为堆的一个逻辑部分。

HotSpot团队将垃圾收集器的分代收集理论扩展至方法区,使用永久代来实现方法区,省去了编写专门的针对方法区的内存管理代码。这种设计使方法区容易遇到内存溢出问题,同时会使HotSpot虚拟机在少数情况下与别的虚拟机有不同的表现。
后续HotSpot舍弃了永久代的设计,移出永久代里的字符串常量池、静态变量放至堆中。采用本地内存来实现方法区,更名永久代为元空间,在该区域保存类型信息(主要)、代码缓存、运行时常量池等数据

字符串常量池运行时常量池有什么区别呢?
在还存在永久代的设计方案里,字符串常量池是被包括在运行时常量池中的,JDK1.7后字符串常量池就被移至堆中。
运行时常量池作为方法区的一部分,存放着Class文件中的常量池表(Constant Pool Table)这项信息。这张表里存放着编译期生成的各种字面量与符号引用(包括由符号引用翻译出来的直接引用)。
字符串常量池类似一个缓存区。考虑到大量频繁地创建字符串,会影响到程序的性能,因此专门为字符串常量开辟了字符串常量池,用户创建字符串常量时,首先查询常量池中是否存在该字符串,如果存在直接返回引用;不存在时,创建新的字符串常量并放入池中。

方法区的内存,也可以固定大小或者可扩展大小,该区域甚至可以选择不实现垃圾收集。这块区域的垃圾收集目标主要是针对常量池的回收和对类型的卸载。如果方法区无法满足新的内存分配需求,会抛出OutOfMemoryError异常

1.1.6 直接内存(Direct Memory)

直接内存不属于虚拟机运行时数据区的一部分。

某些机制(如,NIO)可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,避免在Java堆和Native堆中来回复制数据

直接内存不受Java堆大小的限制,但仍受本机总内存大小的限制。当程序员根据实际内存情况配置各内存区域大小参数(如,-Xmx)时,遗漏直接内存的使用空间,就容易导致动态扩展时出现OutOfMemoryError

1.2 对象

1.2.1 对象的创建

创建对象(不包括数组和Class对象)通常是通过字节码new指令。

创建流程包括:

  1. 检查这个指令的参数是否能在常量池(方法区)中定位到一个类的符号引用
  2. 检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,必须先执行相应的类加载过程
  3. 为新生对象分配内存(对象所需内存的大小在类加载完成后便可完全确定
  4. 将分配到的内存空间都初始化为零值
  5. 对对象进行必要的设置,即在对象的对象头(Object Header)中存放一些信息(如,GC分代年龄信息、是否启用偏向锁、这个对象是哪个类的实例等)
  6. new指令之后会接着执行invokespecial指令,即<init>()方法,按照程序员的意愿对对象进行初始化(Java编译器会在遇到new关键字的地方同时生成new指令与invokespecial指令)

其中第3步,在堆上为对象分配空间有两种方式:
1.

1.2.2 对象的内存布局

1.2.3 对象的访问

1.3 内存溢出

2 垃圾回收

3 工具检测

4 参考文献

  1. 参考书籍:《深入理解Java虚拟机》
  2. 深刻理解运行时常量池、字符串常量池 - 掘金 (juejin.cn)

标签:Java,常量,虚拟机,线程,内存,方法
From: https://www.cnblogs.com/jiziti/p/17369150.html

相关文章

  • 虚拟机环境下,LVS DR模式实现(极简模式)
    环境准备总共使用5台虚拟机进行模拟实现:Client1台,用以模仿访问服务的终端路由服务器1台,用以连接LVS集群和进行访问转发LVS服务器1台后端Web服务器2台IP分配和网络拓扑如下:注:共有3个网段:客户端访问(192.168.0.0),访问LVS服务器,Web服务器,路由器所用的RIP网段(192.168.127.0),VIP网段(19......
  • Intel Pentium III 512MB内存 i815集显上安装Ubuntu Server 14.04
    自己的御用奔腾IIIPC,接口齐全,准备安装UbuntuServer14.04i386,继续发挥余热,物尽其用。 基本配置:CPU:IntelPentiumIII1000MHz,256KBL2,133MHzFSB,0.18um,1.75v,Coppermine-TRAM:512MBSDRAM,PC133GPU:Inteli82815IGPHDD:128GBSSD, withSATAtoIDEa......
  • Java基础
    目录Java基础注释标识符数据类型变量包机制JavaDoc生成文档Java基础注释单行//,多行/*注释/文档注释/**@Description*@Author*/标识符数据类型longnum=30L;floatnum=10.1F;最好完全避免用浮点数进行比较(用Bigdecimal)强制类型转换(不能对boolean转换):(类型)......
  • java基于springboot+vue非前后端分离的影城管理系统、影院销售管理系统,附源码+文档+PP
    1、项目介绍本影城管理系统主要包括二大功能模块,即用户功能模块和管理员功能模块。(1)管理员模块:系统中的核心用户是管理员,管理员登录后,通过管理员功能来管理后台系统。主要功能有:首页、个人中心、用户管理、电影类型管理、放映厅管理、电影信息管理、购票统计管理、系统管理、订......
  • java操作map集合
    java操作map集合importjava.util.HashMap;importjava.util.Map;publicclassMapExample{publicstaticvoidmain(String[]args){//创建一个HashMap对象Map<String,Integer>map=newHashMap<>();//添加键值对......
  • JAVA方法详解
    JAVA方法详解方法是程序中的最小执行单元 形参:形式参数,是指方法定义中的参数实参:实际参数:方法调用中的参数 有返回值的定义格式:publicstatic返回值类型方法名(参数){方法体;return返回值;}调用格式:直接调用赋值调用输出调用方法的注意事项方法......
  • Java 注解
    什么是Java的注解如何在Java中定义一个注解注解通过@interface关键字进行定义。public@interfaceTestAnnotation{}上面的代码就创建了一个名字为TestAnnotaion的注解。那么如何使用这个注解呢?下面的代码创建一个类Test,然后在类定义的地方加上@TestAnnotation......
  • idea创建SpringBoot项目报错For artifact {mysql:mysql-connector-java:null:jar}: Th
    Forartifact{mysql:mysql-connector-java:null:jar}:Theversioncannotbeempty.报错如图:pom.xml文件如图:添加版本号:就好了......
  • 关于java中的super
    首当其冲先说一下super的用途和含义。他是用于调用一些被重写的方法。这里还可以复习一下子这个重写:重写是把新的方法放在被重写的方法前面。在被重写的子类中,优先调用重写后的方法。但是如果想要调用原本未被重写的方法的话,就需要super了。如上的代码中ChildClass为FatherClass......
  • Java-方法重载
    方法重载同一个类中,多个方法的名称相同,但是形参列表不同。方法重载的形式同一个类中,方法名称相同、形参列表不同形参的个数、类型、顺序不同形参的名称无关方法重载的调用流程当程序调用一个重载方法时,编译器会根据参数列表的不同自动匹配最合适的方法,这种机制叫做方法重......