JVM运行时区的介绍

本文详细介绍了JVM的内存区域,包括程序计数器、虚拟机栈、本地方法栈、Java堆、方法区(包括运行时常量池)等,强调了各区域的作用、特性以及与内存问题的关系,帮助读者更好地理解和排查Java内存问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

了解虚拟机的作用

java内存管理是交给了jvm在做

而即使有jvm管理内存还是会出现内存相关的问题

这时候如果不了解jvm的知识,对于内存相关的问题排查就很困难

 

压缩

能更好的解决内存相关的问题

 

程序计数器

用途

当前线程的执行的字节码行号指示器

必要性

java多线程中每个线程执行到的字节码行数不一样

而一个时刻只能有一个线程执行

线程切换的时候我们要知道上cpu的线程应该执行那一条字节码

我们就通过程序计数器来获知

特点

内存很小

此内存区域是唯一个jvm没有规定OutOfMemoryError的地方

每个线程都拥有自己独立的程序计数器

这是线程私有的内存

注意

线程执行的是java方法这个计数器存的就是正在执行的虚拟机字节码指令地址

如果是native方法那就是空值

扩展

指令跳转的逻辑是由字节码指示器来完成的

字节码解释器修改程序计数器的值来选取下一条要执行的指令

 

 

虚拟机栈

必要性

执行java的方法一定会用到方法执行所需要的数据

这些方法执行所需要的数据就存在虚拟机栈中

 

压缩

是java方法执行的内存模型

操作

每个方法执行都会往虚拟机栈中压一个栈帧

栈帧里面存放的内容有

局部变量表

八大基本类型

基本类型的值也存在栈中

对象的引用

对象起始地址引用指针/对象句柄/对象相关的位置

我猜引用指针是更真实的

句柄是被包装了的引用指针

相关位置可能不是起始位置吧

引用的实例存在堆中 new String() 存在堆中

returnAddress

指向一条字节码指令的地址

我猜是方法返回值要到的指令

这个和方法出口好像有点像哦

操作数栈

计算各种值用到的栈

动态链接

存放自己属于哪个方法的引用

方法出口

异常状况

虚拟机栈动态扩展的过程中申请不到内存了 : OutOfMemoryError

也就是系统内存不够了

检查一下系统内存情况

虚拟机栈申请的内存超出虚拟机的孕育了: StackOverflowError

也就是方法调用数量太多虚拟机都觉得过分了

看看是不是方法递归调用过深之类的

注意

局部变量表的内存在编译器完成分配

也就是说方法运行时局部变量表的内存大小已经固定了

 

 

操作数栈

操作数栈就是一个有栈操作的数组

通过压入和弹出来计算各种值

有点类似后缀表达式的栈的实现的感觉

比如 加法就是 压入两个相加的值 遇到加号 弹出两个值 相加 然后再压入 等后面的表达式…

 

 

本地方法栈

和虚拟机栈非常相似

最大的区别就是虚拟机栈是为java方法服务的

本地方法栈,顾名思义就是为本地方法服务的

 

 

对象句柄

就相当于引用

Stirng str;

str就是一个句柄

这个句柄可以指向对象也不指向对象

 

 

java堆

作用

存放对象实例的内存空间

数组也算

字符串常量池是java堆中特殊的一个区域

存放都是字符常量对象

分类

按垃圾回收算法分

新生代

老年代

按内存分配来分

多个线程私有的分配缓冲区

特点

可以存放在物理不连续的空间

逻辑上连续即可

就像磁盘空间一样

物理上是这里存一点那里存一点

但是操作系统中看到是连续的

是java虚拟机内存管理中最大的一块

所有线程共享

堆中存的全是对象实例

但不能说所有对象实例都存在堆中

因为随着技术的发展,某些实例能存在栈中了

这是java虚拟机规范中描述过的

异常

堆中内存不足完成实例分配而且无法扩展内存的时候

OutOfMemeroryError

 

 

 

java 中数组是对象吗?

是的

有和对象创建一样的 new的形式

可以用 instanceOf 来测试类型

 

 

如何判断 一个变量是否属于某个基本类型?

代码

static boolean isInt(Object o){

//基本类型传递过来的时候会自动转换为其包装类型

//基本类型和其包装类型都是一一对应的

if(o instanceof Integer){

return true;

} else {

return false;

}

}

 

public static  void main(String[] args) {

int i = 4;

System.out.println(isInt(i));

 

double j = 5;

System.out.println(isInt(j));

}

 

 

 

方法区

储存

已经被虚拟机加载的类信息

常量

静态变量

即使编译器编译后的代码

 

压缩

储存类信息、常量、静态变量、编译后代码

特点

线程共享

永久代

JDK1.7 之前 HotSpot虚拟机用永久代实现了方法区

用管理java堆的办法管理方法区

但是不是好方法被逐渐淘汰趋势

比如JDK1.7 中HotSpot 就把字符串常量移出了永久代

因为容易导致内存溢出

因为永久代有内存上限对方法区可能低了

比如J9和Jrockit虚拟机就不是用永久代实现的方法区

上限是操作系统分配的进程上限

不同虚拟机对方法区实现的差异导致了

String.intern()方法在不同虚拟机下的表现不同

JDK1.8 HotSpot 用元空间实现方法区,堆也不存在永久区的概念了

元空间不在虚拟机中,而是在本机内存

不论是永久代还是元空间还是java堆都是方法区的一种实现

注意

永久代并不永久,虽然垃圾回收比较少,但是还是会回收

主要是回收常量池和类型卸载

历史上sun的BUG列表中就出现过几次重大的BUG

都是由于方法区没能完全回收导致了内存泄露

而且我觉得方法区听起来好像是存储方法运行的信息的

但是并不是,储存方法运行的信息是java栈做的事

那为啥叫方法区呢……

异常

方法区无法满足内存分配的需求时候 OutOfMemoryError

 

 

运行时常量池

储存

类加载后Class文件的常量池部分

Class文件的常量池有时被简称常量池

存放编译器生成的字面量和符号引用

java还有一个字符串常量池

运行期产生的常量

这部分的字符串常量可能在JKD1.7及之后就被移除到方法区之外了

书中说的是intern引入的运行时字符串常量

我不止到是否还有其他的常量能在运行时产生

因为常量一般的产生依靠final定义

这部分应该是上面的类加载之后的Class常量部分

或者其他常量可能是方法调用之后在方法中产生的常量

方法调用中产生的常量类加载的时候应该是不知道的

 

特点

是属于方法区的

为此也受方法区的约束

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值