了解虚拟机的作用
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常量部分
或者其他常量可能是方法调用之后在方法中产生的常量
方法调用中产生的常量类加载的时候应该是不知道的
特点
是属于方法区的
为此也受方法区的约束