JVM运行时的数据区域

81 阅读5分钟

JVM-运行时的数据区

问:

1、java的内存分为哪几个部分?

2、那部分内存会溢出?

3、JDK7与8之间有什么区别?

为什么了解学习这部分内容?

1、了解JVM内存结构

2、分析内存问题,产生原因

3、解决内存问题,堆JVM内存调优

总览

从线程共享与否分为

线程不共享:程序计数器、java虚拟机栈、本地方法栈

线程共享:方法区、堆

线程不共享

一、程序计数器

程序计数器可以控制程序指令的进行,实现分支、跳转、异常等逻辑。

程序计数器是不会出现内存溢出的

关于程序计数器不会溢出的解释:

程序计数器仅仅只是一个运行指示器,它所需要存储的内容仅仅就是下一个需要待执行的命令的地址,无论代码有多少,最坏情况下死循环也不会让这块内存区域超限,因为程序计数器所维护的就是下一条待执行的命令的地址,所以不存在OutOfMemoryError

这里有张图:

6..png

二、Java虚拟机栈

java虚拟机栈维护着:

局部变量表、操作数栈、帧数据

1)、java虚拟机栈-局部变量表

参数

1、起始pc

字节码指令生效的起始位置

2、长度

字节码指令生命周期长度

3、序号

字节码指令在变量表中的位置

7.png

局部变量表存放参数顺序

1、实例对象this

2、方法参数

3、方法体中的变量 8.png 看了前面的内容,可能已经对局部变量表有了一定的了解,那么下面这段代码,在局部变量表中是如何存储的呢?

image-20240523214721447.png 你对了吗?

下面这张图,解释的很详细

9.png

2)、JVM虚拟机栈-操作数栈

jvm执行指令过程中用栈式结构存放中间数据,在编译器就可以确定该栈的最大深度,为其分配内存大小

10.png

3)、帧数据

1、动态连接

11.png

2、方法出口

12.png

3、异常表

13.png

栈内存溢出

运行程序时可能会出现栈溢出异常

image-20240528093916347.png

那就可以通过手动指定栈的大小来解决:

为虚拟机栈设置栈大小

指令:

-Xss1024k 

K-KB M-MB G-GB 没有就是一字节

14.png

注意事项

不同的操作系统下栈最大最小范围有所不同

15.png

三、本地方法栈

image-20240522182425151.png

线程共享

一、

  • 用来存放创建出的对象
  • 最容易出现内存溢出的内存区域
  • 模拟堆内存溢出

image-20240524164116872.png

堆内存需要关注的三个值

used:已使用的堆内存

total:已经分配可用的堆内存

max:可以分配的最大堆内存

image-20240524164220808.png

java虚拟机为堆内存扩容

image-20240524165404254.png 扩容上限

image-20240524165426740.png

那是否扩容上限上限时,userd==max?

其实不然 这里used并不会达到max的最大值 具体溢出的判断条件比较复杂

image-20240524165753386.png

设置堆大小

Xmx:max

Xms:total

image-20240524170028468.png

实际上堆内存与实际分配的大小不是完全一致的,这跟GC有关系,后面的文章会讲解

image-20240524170222757.png

给在最后的建议,在设置堆内存时,尽量将Xmx与Xms设置为相同值。

二、方法区

用来存放基础信息的位置,线程共享

方法区也可能出现内存溢出的情况

包括三部分内容:

类的元信息、运行时的常量池、字符串常量池

方法区是一个虚拟的概念,JDK7及以前,JDK8及以后方法区的存储存在着变化

JDK7以前方法区直接存放在堆空间中,JDK8及以后存放在堆以外的元空间中,这块空间独立于虚拟机直接与操作系统相关。

下面是各JDK版本下的运行时的数据区域 堆和方法区

永久代:JDK6 ---> Java 的内存中有一块称之为方法区的部分

JDK6

image-20240524170320633.png

JDK7

image-20240527201652423.png

JDK8

image-20240527201703873.png

1、元信息

一般称为instanceClass对象,在类的加载阶段完成

元信息中包括信息如下 image-20240526162813453.png

但是常量池方法 java虚拟机会额外用一段内存区域来存放,这里只是存放了它的引用地址

2、运行时常量池

image-20240526202710058.png

建议设成256MB

3、字符串常量池

stringTable--字符串常量池

字符串常量池存放着字符串内容例如:"123","abc"

通过对象new出来的String内容,直接放在堆内存中 变量的地址指向堆。

而直接赋值的字符串存放在常量池,变量指向常量池。

c16f88bec23ff654563b3e0292980db9.jpeg

常量池与方法区有什么联系?

下面分别介绍了JDK7及以前JDK8开始的字符串常量池存储位置发生的变化

c104731370a4a959ca50157a645648e2-1716905438951.jpeg

下面通过一道练习题来了解字符串常量池的存储

54d64bbb0e72e18ad6743b63465bf812.jpeg

字节码指令示例:

82e02c3fc6905b7dc090f4b90881143c.jpeg

通过字节码指令发现,a和b通过stringBuilder拼接后存储到堆内存中,d指向堆中

intern()方法

JDK6

fedd800d62896fd2bce49dc287561cfe.jpeg

JDK7及以后

04974f2f85d225ea0dcd54c0997260cb.jpeg

这里有一点需要注意的是,”java“这个字符串会被java虚拟机会被直接加载到字符串常量池中。

所以JDK6的结果是:false、false

JDK7以后:false、true

直接内存

直接内存不属于java虚拟机运行的内存区域

image-20240528232026742.png

自己如果要实现直接内存上的数据,可以通过ByteBuffer image-20240528232205026.png

指定直接内存大小

image-20240527200845236.png