虚拟机的前世今生和java内存区域
1.虚拟机的前世今生
.java文件编译成字节码.class文件,这样可以在jvm中运行,其中还需要借助jre里面已经定义好的java类库。
而jvm中又包含classloader、字节码解释器、jit编译器和执行引擎。
.class是字节码文件,jvm可以将很多语言转换成字节码文件,从而实现在多平台上运行(windows、linux、unix、andriod、mac)
1.1常见的jvm实现
- hotspot-oracle
- jrocket-oracle
- j9-ibm
- taobaovm-taobao(hotspot的深度定制版本)
- liquidvm-bea
- zing
1.2jvm的知识体系
-
内存结构
1.垃圾回收
2.性能调优
3.jvm自身优化技术
4.执行引擎
5.监控工具
6.类文件结构
7.类加载
2.jvm的内存区域
2.1运行时数据区域
- 线程共享区
- 方法区
- 运行时常量池
- 堆
- 方法区
- 线程私有区
- 虚拟机栈
- 本地方法栈
- 程序计数器
- 直接内存
- 总共8g内存,jvm占用3g,直接内存还剩5g
2.2虚拟机栈
- 存储当前方法所需要的数据、指令、返回地址
- 大小限制 -Xss,一般是1m,不同虚拟机不一样
- 先进后出
- 栈溢出,递归无出口时会造成栈溢出(stackOverFlowError)
2.3 方法区
-
方法区用以将class里面的类加载到类加载器中
- 类信息
- 常量
-
永久代(jdk1.7里面方法区的名称)和元空间(jdk1.8里面方法区的名称)。
-
类加载
-
class文件需要解析,把变量放到常量池中
-
√运行时常量池()
-
class-- 符号引用 -》直接引用(真实的内存地址)
{
Tool.hashcode(); ~13号
}
-
-
静态常量池(javap -c)
-
字面量:String a = “b”;b就是字面量
-
-
-
方法区是静态的,创建后经常使用的
2.4堆
- 堆是保存对象的,是动态的,gc的主要区域。
2.5 直接内存(堆外内存)(EHcache)(中间件)
- NIO中的DirectByteBuffer
- 使用直接内存需要手动回收资源
- unsafe类中allocateMemory方法可以分配内存
3.java方法的运行于虚拟机栈
3.1栈帧
- 局部变量表(基础数据、引用对象的字段)(0是this)(iconst_1对应于1)(iconst_2对应2)
- 操作数栈(重点)(执行引擎的一个工作区)(cpu+主内存+缓存)(jvm属于mini版本的操作系统)(对应的就是jvm执行引擎+堆、栈+操作数栈)(javap -v可以对class文件进行反汇编)(腾讯云java虚拟机 jvm字节码 指令集(https://cloud.tencent.com/developer/article/1333540))(iadd后为什么要入栈,因为cpu不会暂时保存数据,所以操作数栈起到了一种中间缓存的作用,在执行引擎中生成的中间数据全部保存到操作数栈中)(如果局部变量表中的数据要返回,必须先入栈,因为执行引擎只能与操作数栈交互)
- 动态连接(需要结合class文件和执行引擎来讲)
- 完成出口(返回地址)(class文件反汇编后,前面会有0、1、2、3、4、5…,这相当于c、c++中的偏移量,可以看出字节码的地址和行号)
3.2栈帧执行对内存区域的影响
-
运行java方法,生成一个虚拟机栈,入一个栈帧,方法中如果调用其他方法,又会压入一个栈帧,其中包括局部变量表、操作数栈
-
字节码指令iadd:将数据从操作数栈出栈,在执行引擎进行加法运算,然后自动入到操作数栈
-
程序计数器(cpu要多核执行):字节码的行号,程序的反跳和异常跳出都需要程序计数器,
- √操作系统:CPU时间片轮转机制。CPU1s中可以干10w个指令,因此将时间进行了切片,假设被切成了1000片,每一片不一定是连续的。
- 程序计算器是操作系统中唯一不会oom的。因为它只是单纯的计数。
3.3 本地方法栈–加了native的方法
- 在c/c++中实现,需要本地方法栈执行。
- 虚拟机规范,因为虚拟机里面方法的执行有上面这样一套入栈出栈的思路,所以需要一套单独的同模式的框架来实现相同的处理。
答疑
1.一个线程只有一个程序计数器
2.iload_3入栈后,这个数值仍然存在于局部变量表中
3.时间片轮转,反汇编的行号为什么不连续?因为有的指令需要多个字节的操作数。(http://m.bubuko.com/infodetail-1884259.html?cf_chl_jschl_tk=02b6ebdd51151a04341cae188624d6b508e2fd50-1598025370-0-AQ9iU1GqGo4JaXQ6VtuLmtOTBaXABtUNjNZuiWWelzLE76-YHB2S8XH6gPsNdAF5ouLuc_jLHeTOPOEXDjWZVcE-valAYgC3obV4xMImrbdrbgfZYTt7b6YSO-66jkfIMHNzCX2g2gT4Dm-bhOkhMUKW0fr_XwxSRlWV6PxDV936EtFejh7R96GkQQfjyTefvyoRG9RMj-62heDsxSnte8ptJgOqEU0WT7OGqW-SgKuXPgSKBJCYk-yjJjWupRGGSImvXqBw-vYUMAshcvfsiQFDRik4O73YCmColjLTAyS_pIrae-sbN94jAEBLK8Bxsw)
4.运行时常量池中符号引用变为直接引用是什么意思?
5.栈溢出的主要原因是不断的压栈帧
6.栈帧出栈后不需要回收,这块区域就没有了,跟随线程销毁
7.ireturn到哪里?属于一个指令,根据返回值类型进行区分返回
8.不是操作数栈溢出,指令集设计有问题,是虚拟机栈溢出
9.符号引用,不指代地址,jvm运行后当变成直接引用,就代表了具体的地址。