JVM是运行在操作系统之上的,和硬件没有直接关系
类加载器,运行时数据区域,
运行时数据区域:堆,虚拟机栈,方法区,程序计数器,本地方法栈
堆:存储对象
虚拟机栈(FILO:先进后出)(线程独立):例如执行main方法,虚拟机栈会划出一块栈帧空间,存储main方法,先压入栈,然后main方法执行中有exe()方法,再开辟一块栈帧空间存储exe()方法信息,再压入栈。执行完exe()方法,则把此方法抛出。因为栈是在线程创建时创建,它的生命周期即是线程的生命周期,当线程结束,内存释放,所以不存在垃圾回收问题
存储内容:
本地变量:输入参数 和 输出参数 以及方法内的变量
栈操作:记录入栈,出栈的操作
栈帧数据:包括类文件,方法等
方法区(1.8之前叫持久代或永久带,1.8之后叫元空间):存储静态变量,常量,类元信息,运行时常量池等
程序计数器:当前线程执行的行数
本地方法栈:存放native的方法,此类接口由java调用底层的c实现
===========
1.8之后方法区使用的内存是直接内存,即非虚拟机内存。若服务器总内存16G内存,虚拟机分配了1G内存,直接内存是从另外15G里使用
栈管运行,堆管存储
线程共享区域:方法区,堆
线程私有区域:虚拟机栈,程序计数器,本地方法栈
查看更可读的字节码文件:找到XXX.class文件,右键,找到Open in terminal,在弹出的输入框中输入:javap -c XXXX.class
若保存成文件:javap -c XXXX.class > XXXX.txt
动态链接,可查看格式:javap -v XXXX.class > XXXX.txt
如上即是虚拟机指令
程序计数器会从0开始,往下指向
冒号后面的即是虚拟机指令,可从官网的虚拟机指令中查找对应的意思
栈帧:
局部变量表:存放局部变量
操作数栈(FILO):例如 a= 30; Int类型的30先放到操作数栈,然后a变量在局部变量表,然后把30弹出操作数栈,将30赋值给a
动态链接:线程中很多方法对应的指令码,指令码是程序运行中动态生成的。因此可以在一个方法中的方法名称调用到另一个方法
方法出口:存放上一个方法调回信息等
堆:
年轻代(Eden,Survivor(From,To))和老年代,一般默认年轻代占1/3,老年代占2/3内存。年轻代州Eden占8/10,From占1/10,To占1/10.
对象产生,先存入Eden中,若Eden满了,会触发 执行引擎 执行minor gc(新生代的gc),第一次垃圾回收,会回收Eden中的对象,若未被回收,则数据存入From中(若数据过大,超过From的50%,则直接存入老年代),并且分代年龄+1。垃圾回收第二次,回收内存Eden和From,不需要被回收的对象存入To内存中,并且分代年龄再+1,From内存清空。第三次垃圾回收,回收内存Eden 和 To,不需要被回收的对象存入From内存中,分代年龄再+1。如此循环,分代年龄15次还存活的对象存入老年代。
若老年代满了,则会执行full gc/magor gc,会STW(stop the word)
JNI:java native interface
类元信息
对象头:Mark Word(标记字段),Klass Pointer(类型指针),数组长度
标记字段: 自身运行时的数据: 哈希值,GC分代年龄,锁状态标记,线程持有锁,偏向线程Id,偏向时间戳
类型指针:类的元数据的指针
数组长度:只有数组中对象才有
=====
GC Root 根节点:类加载器,Tread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等
java visualVM 软件可以查看VM运行窗口,包括eden,survivor(from,to),老年代
JVM调优目的:减少full gc的次数,减少一次full gc的时间