JVM深入原理(七)(二):运行时数据区

简介: 堆的作用:存放对象的内存空间,它是空间最大的一块内存区域.栈上的局部变量表中,可以存放堆上对象的引用。静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享。堆的特点:线程共享:堆中的对象都需要考虑线程安全的问题垃圾回收:堆有垃圾回收机制,不再引用的对象就会被回收方法区的概述:方法区是存放基础信息的位置,线程共享,主要包括:类的元信息:保存了所有类的基本信息运行时常量池:保存了字节码文件中的常量池内容静态常量池:字节码文件通过编号查表的方式找到常量。

 

JVM系列文章

  1. 深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器_eden used total max-CSDN博客
  2. JVM深入原理(一+二):JVM概述和JVM功能-CSDN博客
  3. JVM深入原理(三+四):JVM组成和JVM字节码文件-CSDN博客
  4. JVM深入原理(五):JVM组成和JVM字节码文件-CSDN博客
  5. JVM深入原理(六)(一):JVM类加载器-CSDN博客
  6. JVM深入原理(六)(二):双亲委派机制-CSDN博客
  7. JVM深入原理(七)(一):运行时数据区-CSDN博客
  8. JVM深入原理(七)(二):运行时数据区-CSDN博客
  9. JVM深入原理(八)(一):垃圾回收-CSDN博客
  10. JVM深入原理(八)(二):垃圾回收-CSDN博客

目录

7.5. 运行时数据区-本地方法栈

7.6. 运行时数据区-Java堆

7.6.1. 堆-概述

7.6.2. 堆-空间

7.6.3. 堆-设置大小

7.6.4. 堆内存溢出

7.6.5. 堆内存诊断

7.7. 运行时数据区-方法区

7.7.1. 方法区-概述

7.7.2. 方法区-实现

7.7.3. 方法区-内存溢出

7.7.4. 方法区-字符串常量池

7.7.4.1. 字符串常量池-概述

7.7.4.2. 字符串常量池-工作流程

7.7.4.2.1. 案例1

7.8. 直接内存

7.8.1. 直接内存-概述

7.8.2. 直接内存-工作流程

7.8.3. 直接内存-调整内存大小

7.8.4. 直接内存分配释放原理


7.5. 运行时数据区-本地方法栈

  • 本地方法栈的作用:Java虚拟机栈存储了Java方法调用时的栈帧,而本地方法栈存储的是native本地方法的栈帧。
  • 本地方法栈的栈空间:在Hotspot虚拟机中,Java虚拟机栈和本地方法栈实现上使用了同一个栈空间。本地方法栈会在栈内存上生成一个栈帧,临时保存方法的参数同时方便出现异常时也把本地方法的栈信息打印出来

    image.gif 编辑

7.6. 运行时数据区-Java堆

7.6.1. 堆-概述

  • 堆的作用:存放对象的内存空间,它是空间最大的一块内存区域.栈上的局部变量表中,可以存放堆上对象的引用。静态变量也可以存放堆对象的引用,通过静态变量就可以实现对象在线程之间共享。

    image.gif 编辑
  • 堆的特点:
  • 线程共享:堆中的对象都需要考虑线程安全的问题
  • 垃圾回收:堆有垃圾回收机制,不再引用的对象就会被回收

7.6.2. 堆-空间

  • 堆空间的值:used,total,max,如果不设置任何的虚拟机参数,max默认是系统内存的1/4,total默认是系统内存的1/64.在实际应用中一般都需要设置total和max的值。

    image.gif 编辑
  • used:当前已使用的堆内存
  • total:java虚拟机已经分配的可用堆内存
  • max:java虚拟机可以分配的最大堆内存
  • 随着堆中的对象增多,当total可以使用的内存即将不足时,java虚拟机会继续分配内存给堆。如果堆内存不足,java虚拟机就会不断的分配内存,total值会变大。total最多只能与max相等。

    image.gif 编辑

7.6.3. 堆-设置大小

  • 要修改堆的大小,可以使用虚拟机参数-Xmx(max最大值)和-Xms(初始的total)。
  • 语法:-Xmx值-Xms值
  • 单位:字节(默认,必须是1024的倍数)、k或者K(KB)、m或者M(MB)、g或者G(GB)
  • 限制:Xmx必须大于2MB,Xms必须大于1MB

7.6.4. 堆内存溢出

程序运行时不断地创建新的对象就会出现堆内存溢出OOM

java.lang.OutOfMemoryError: Java heap space

image.gif 编辑

7.6.5. 堆内存诊断

  1. jsp工具:查看当前系统有哪些Java进程
  2. jmap工具:查看堆内存占用情况,使用时加上参数-heap javaid,查看指定进程的堆内存占用情况
  3. jconsole图形化界面查看内存占用情况
  4. jvisualvm图形化界面查看内存占用情况

7.7. 运行时数据区-方法区

7.7.1. 方法区-概述

  • 方法区的概述:方法区是存放基础信息的位置,线程共享,主要包括:
  • 类的元信息:保存了所有类的基本信息
  • 运行时常量池:保存了字节码文件中的常量池内容
  • 静态常量池:字节码文件通过编号查表的方式找到常量
  • 运行时常量池:当静态常量池加载到内存中后,可以通过内存地址快速查找常量池中的内容
  • 字符串常量池:保存了字符串常量

7.7.2. 方法区-实现

  • 方法区概念:方法区是Java虚拟机规范上的虚拟设计理念,每款JVM实现各不相同
  • 不同版本的实现:

    image.gif 编辑

    image.gif 编辑
  • JDK7及之前的版本将方法区存放在堆区域中的永久代空间,堆的大小由虚拟机参数来控制。
  • JDK8及之后的版本将方法区存放在元空间中,元空间位于操作系统维护的直接内存中,默认情况下只要不超过操作系统承受的上限,可以一直分配。

7.7.3. 方法区-内存溢出

  • JDK7的方法区内存溢出:永久代内存溢出,Java.lang.OutofMemoryError:PermGen space,设置永久代大小参数-XX:MaxPermSize=8m
  • JDK8的方法区内存溢出:超过操作系统内存上限后,才会出现元空间内存溢出,有可能影响其他程序的内存,java.lang.OutofMemoryError:Metaspace,设置元空间大小参数XX:MaxMetaspaceSize=8m

7.7.4. 方法区-字符串常量池

7.7.4.1. 字符串常量池-概述

  • 字符串常量池的作用:用于存储在代码中定义的常量字符串
  • 字符串常量池和运行时常量池的关系:早期字符串常量池是运行时常量池的一部分存储位置也是一致的,后期将字符串常量池和运行时常量池做了区分

    image.gif 编辑

7.7.4.2. 字符串常量池-工作流程

  • 利用代码来解释字符串常量池工作流程:

    image.gif 编辑
7.7.4.2.1. 案例1

------源代码------

image.gif 编辑

------执行结果------

  • s3 == s4:s3指向的是字符串常量池中的ab字符串,s4指向的是堆中的ab字符串对象,所以两者地址不相等为false
  • s3 == s5:s3指向的是字符串常量池中的ab字符串,s5的字符串a和字符串b的拼接在编译期直接完成,指向的是字符串常量池中的ab字符串,所以两者地址相等为true
  • s3 == s6:s3指向的是字符串常量池中的ab字符串,s6的值是s4未放入字符串常量池而返回的字符串常量池中ab字符串,所以两者地址相等为true

------字节码------

image.gif 编辑

------代码解析------

  1. ldc #2 <a>:从常量池中获取字符串a的地址压入操作数栈
  2. astore_1:将操作数栈中的值(a)弹出放入局部变量表1号位置(变量s1的位置)
  3. ldc #3 <b>:从常量池中获取字符串b的地址压入操作数栈
  4. astore_2:将操作数栈中的值(b)弹出放入局部变量表1号位置(变量s2的位置)
  5. ldc #4 <ab>:从常量池中获取字符串ab的地址压入操作数栈
  6. astore_3:将操作数栈中的值(ab)弹出放入局部变量表1号位置(变量s3的位置)
  7. new #5 <java/lang/StringBuilder>:创建StringBuilder对象
  8. dup
  9. invokespecial #6 <java/lang/StringBuilder.<init>>:初始化StringBuilder为空字符串""
  10. aload_1:加载局部变量表中的1号位置的值(a)到操作数栈
  11. invokevirtual #7 <java/lang/StringBuilder.append:调用StringBuilder的append拼接刚刚操作数栈加载的字符串a
  12. aload_2:加载局部变量表中的1号位置的值(b)到操作数栈
  13. invokevirtual #7 <java/lang/StringBuilder.append:调用StringBuilder的append拼接刚刚操作数栈加载的字符串b
  14. invokevirtual #8 <java/lang/StringBuilder.toString:调用StringBuilder的toString方法将StringBuilder对象转换成String对象存入堆内存中
  15. astore 4:弹出操作数栈中的数据(ab字符串对象)放入局部变量表中的4号位置(变量s4的位置)
  16. ldc #4 <ab>:从常量池中获取字符串(ab)的地址压入操作数栈
  17. astore 5:弹出操作数栈中的数据(ab)放入局部变量表中的4号位置(变量s5的位置)
  18. aload 4:加载局部变量表中的4号位置的值(ab)到操作数栈
  19. invokevirtual #9 <java/lang/String.intern>:调用String的intern方法尝试将ab字符串加入到字符串常量池中,判断字符串常量池中是否有ab字符串,有则直接返回的字符串常量池中的ab字符串到操作数栈中,无则将4号位置的引用(ab字符串对象)转换到字符串常量池中(引用变为字符串常量池中)并返回字符串常量池中的引用到操作数栈中
  1. intern方法会主动将调用方法的字符串对象放入字符串常量池中,判断字符串常量池中是否有这个字符串常量池.
  1. JDK8中有则直接返回字符串常量池中的字符串常量,无则将当前堆中的字符串对象引用转到字符串常量池中(堆中的字符串对象之后会被垃圾回收,因为无人引用了),然后返回字符串常量池中的引用
  2. JDK6中有则直接返回字符串常量池中的字符串常量,无则将当前堆中的字符串对象复制一份到字符串常量池中(相当于两份引用,原来的并不会转变到字符串常量池中),然后返回字符串常量池中的引用
  • JDK1.6中StringTable没有这个对象时会复制对象再放入,本质上是两个对象
  1. astore 6:弹出操作数栈中的数据(ab)放入局部变量表中的6号位置(变量s6的位置)

image.gif 编辑

  • String s1 = new String("abc"):new关键字创建一个abc字符串对象,当代码编译成字节码后,abc字符串会存入静态常量池中,当程序运行字节码读取到内存后,abc字符串就会存入字符串常量池中,由于字符串对象是new关键字创建出来的,所以这个对象会存储在堆内存中,在栈内存中使用s1局部变量去保存堆内存中abc的地址
  • String s2 = "abc":并没有创建字符串对象,而是直接去字符串常量池中找是否有abc字符串,有则直接返回,无则在字符串常量池中创建一个abc字符串再返回
  • s1 == s2:s1指向的是堆中的abc字符串对象,s2指向的是字符串常量池中的abc字符串

7.8. 直接内存

7.8.1. 直接内存-概述

  • 直接内存的作用:直接内存(Direct Memory)并不在Java虚拟机规范中存在,并不属于Java运行时的内存区域,在JDK4中引入NIO机制使用到了直接内存,主要解决两个问题:
  1. 解决Java堆中的对象垃圾回收时会影响对象的创建和使用的问题
  2. 解决IO操作浪费资源的情况,NIO在堆中可以直接访问直接内存,而不是IO的复制一份数据到Java堆中缓存
  • 直接内存的特点:
  1. 分配回收成本高,同时读写性能也高
  2. 不收JVM内存回收管理

7.8.2. 直接内存-工作流程

  • 传统IO-工作流程:

    image.gif 编辑
  1. Java调用本地方法将CPU状态由用户态转变为内核态(因为Java并不具备磁盘读写的能力)
  2. CPU切换至内核态后,会在系统内存和Java堆内存中各开辟一块缓存区域,系统缓存区分批次读取磁盘文件数据后再复制一份到Java堆缓存区中,完成整个磁盘读取后会Java再次调用本地方法将将CPU状态由内核态转变为用户态
  3. CPU切换为用户态后,Java使用IO读取Java堆缓存区中的数据
  • NIO-工作流程:
  1. Java调用本地方法将CPU状态由用户态转变为内核态(因为Java并不具备磁盘读写的能力)
  2. CPU切换至内核态后,会直接在系统内存中划分一块儿缓冲区直接内存,Java堆和系统可以直接访问直接内存中的数据,这样就不用复制一份到Java堆中了

给直接内存分配太多空间但没有及时回收就会造成直接内存溢出

OutOfMemory:Direct Buffer Memory

7.8.3. 直接内存-调整内存大小

  • 如果需要手动调整直接内存的大小,可以使用-XX:MaxDirectMemorySize=大小
  • 单位k或K表示千字节,m或M表示兆字节,g或G表示干兆字节。默认不设置该参数情况下,JVM自动选择最大分配的大小。
  • 以下示例以不同的单位说明如何将直接内存大小设置为1024KB:
  • -XX:MaxDirectMemorySize=1m
  • -XX:MaxDirectMemorySize=1024k
  • -XX:MaxDirectMemorySize=1048576

7.8.4. 直接内存分配释放原理

  • Java中Unsafe对象完成直接内存的分配回收,回收时需要主动调用UnSafe对象的freeMemory方法
  • Java中的ByteBuffer接口实现类内部使用了Cleaner(虚引用)来监测ByteBuffer对象,一旦ByteBuffer对象被垃圾回收,那么就会有ReferenceHandler线程通过Cleaner的clean方法调用freeMemory方法来释放直接内存
  • 一般JVM调优时,会使用-XX:+DisableExplictGC禁用显示垃圾回收(system.gc()),因为显示垃圾回收是一种full GC,对程序影响大,不仅会回收新生代对象,而且老年代对象一并回收,但是开启禁用之后,直接内存的回收就不会被回收,需要手动去获取UnSafe对象回收直接内存

image.gif 编辑

image.gif 编辑

目录
打赏
0
0
0
0
19
分享
相关文章
JVM深入原理(一+二):JVM概述和JVM功能
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行。
78 0
|
2月前
|
JVM深入原理(三+四):JVM组成和JVM字节码文件
目录3. JVM组成3.1. 组成-运行时数据区3.2. 组成-类加载器3.3. 组成-执行引擎3.4. 组成-本地接口4. JVM字节码文件4.1. 字节码文件-组成4.1.1. 组成-基础信息4.1.1.1. 基础信息-魔数4.1.1.2. 基础信息-主副版本号4.1.2. 组成-常量池4.1.3. 组成-方法4.1.3.1. 方法-工作流程4.1.4. 组成-字段4.1.5. 组成-属性4.2. 字节码文件-查看工具4.2.1. javap4.2.2. jclasslib4.2.3. 阿里Arthas
53 0
|
2月前
|
JVM深入原理(五):JVM组成和JVM字节码文件
类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析。
39 0
JVM深入原理(六)(一):JVM类加载器
目录6. JVM类加载器6.1. 类加载器-概述6.2. 类加载器-执行流程6.3. 类加载器-分类(JDK8)6.3.1. JVM底层实现的类加载器6.3.1.1. 启动类加载器6.3.2. Java代码实现类的加载器6.3.2.1. 扩展类加载器6.3.2.2. 应用程序类加载器6.4. 类加载器-Arthas查看类加载器
38 0
JVM深入原理(六)(二):双亲委派机制
自定义类加载器打破双亲委派机制的方法:复写ClassLoader中的loadClass方法常见问题:要加载的类名如果是以java.开头,则会抛出安全性异常加载自定义的类都会有一个共同的父类Object,需要在代码中交由父类加载器去加载自定义类加载器不手动指定parent会默认指定应用类加载两个自定义类加载器加载同一个类会被认为是两个对象,只有相同的类加载器+想通的类限定名才会被认为是一个对象。
57 0
|
2月前
|
JVM深入原理(七)(一):运行时数据区
栈的介绍:Java虚拟机栈采用栈的数据结构来管理方法调用中的基本数据,先进后出,每一个方法的调用使用一个栈帧来保存栈的组成:栈:一个线程运行所需要的内存空间,一个栈由多个栈帧组成栈帧:一个方法运行所需要的内存空间活动栈帧:一个线程中只能有一个活动栈帧栈的生命周期:栈随着线程的创建而创建,而回收会在线程销毁时进行栈的执行流程:栈帧压入栈内执行方法执行完毕释放内存若方法间存在调用,那么会压入被调用方法入栈,执行完后释放内存,再执行当前方法,直到执行完毕,释放所有内存。
40 0
|
2月前
|
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
323 55
Arthas memory(查看 JVM 内存信息)
Arthas memory(查看 JVM 内存信息)
190 6
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
1458 1
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等