简单整理java虚拟机
时间:2023-07-15 04:06:01 | 来源:网站运营
时间:2023-07-15 04:06:01 来源:网站运营
简单整理java虚拟机:java虚拟机组成部分:类装载子系统、字节码执行引擎、内存模型
程序启动后由类装载系统将我们的.clss字节码文件加载到内存区域,然后通过执行引擎去运行整个字节码文件!
下面主要说说内存模型,它由堆、栈、本地方法栈、方法区、程序计数器组成
其中栈也叫虚拟栈,线程栈(方法执行顺序first in afer out 先进后出)
比如上面整个main方法在运行时候,会开启主线程,java虚拟机就会对线程里面对局部变量做内存分配,让其存储!如果是多线程,那么每个线程有自己独立对栈内存空间,让其存储对应局部变量!
而栈内存区域中还划分了其他内存区域包括栈帧(主要放main方法、count方法等不同方法等内存区域)也就是说java内存中一个方法对应一块栈帧!总结一句,栈首先是虚拟机一块内存,也是一种数据结构,它里面用数据结构栈组织我们的内存数据!其中主要组成部分包括:局部变量表、操作数栈、动态链接、方法出口等
现在对上面程序Math.class执行javap -c Math.class反编译得到jvm指令码,去查询手册看到每行代码执行的方法;
举个例子:比如iconst_1
操作数栈中执行算法操作,可以理解成程序在运行时,一个临时的中转内存区域,也是用的先进后出栈的数据结构!
方法出口是执程序行完方法后,马上返回主方法的位置,即count方法执行完后返回到main 方法到位置,main方法即将执行到程序计数器到数值!
程序计数器就是正准备执行指令码的位置(行号,可以理解成指针)
math是new出来的局部变量,放在栈里的,但是对应的值放在堆里的,两者关系:栈中的math局部变量存的是堆中math值的地址,是一个引用!
栈中有很多对象这种局部变量由指针指向堆中的很多对象!
方法区(jdk8以后叫元空间,以前叫持久代),主要由类装载子系统加载字节码文件加载到位置就是我们到方法区!主要放到是一系列到类元信息(常量、静态变量、类元信息),类元信息是类的组成部分,类也有一个类对象,通过反射,把类的方法,常量组成到部分;
math和math1都是math对象,对象组成部分有个对象头,存储了很多对象信息,在new math对象时候,会在对象头放Math类到地址指针,因为在运行到时候,就是通过整个指针找对应类到指令码;
我需要找到指令码,需要知道是那个类,在new 的时候会存一个地址指针
动态链接:程序运行时,放的是方法对应对类元信息的位置,动态生成的,只有程序在运行的时候
#4由两部分组成,Math类和compute:()方法,在jvm中统称符号;程序执行时候,会把这些静态符号拥有的地址指针符号存到动态链接内存中,说白堆和方法区也有一个联系,有很多指针,堆中很多对象中对象头有一个类型指针指向对象所属类的类元信息的相关的地址!
方法区严格意义上不算java虚拟机内部的内存,它用的操作系统的直接内存;比如:操作系统16G大小,分配java虚拟机内存1G,方法区用的内存是从剩下的15G中拿的!规范上来讲,算内存模型的一块组成部分!
本地方法栈要从本地方法说起,底层有很多native方法,就是我们java的本地方法,底层并不是java语言实现的,是C语言实现的!在程序执行到此类方法会调用C语言对此方法对实现,类似jar包,现在技术发展,已经用很少了!它需要的内存就就在本地方法,也是线程独享的,
堆,调优主要就是对堆进行调优!
假设总共有600M内存空间,按照年轻代:老年代比例=1:2,我们为年轻代分配200M内存空间,老年代400M内存空间,那么eden区大概占140M,From和To区分别占40M;
new出来对象先放在eden区,如果放满了,会做出两种事情:
第一种:
java虚拟机则会执行一次gc(Garbage Collection,垃圾回收),这里是minor gc,会对处理掉无效对对象,当main方法执行完,java进程退出前,栈里已经清空了,但堆里math仍然在,就是没有指针指向了,gc就会处理掉这里math对象;
GC Roors根节点:类加载器、Thread、虚拟站栈当本地变量表,statis成员、常量引用、本地方法栈当变量等
比如:java虚拟机栈等本地变量表也是一种GC Roots根,消亡的时候,没有指针指向堆中的对象,这些对象就成了游离状态,java虚拟机就会回收这些变量;
第二种:
将存活对象挪到Survivor区的from区,如果from区放满了,也会出发GC,将存活对象挪到To区,涉及到了一系列到算法包括:垃圾收集算法,标记清除算法,复制算法等,如果To区满了,也会执行GC,
每个对象都存在一个分代年龄,存在对象头里,如果它在Survivor区经历过一次minor gc,分代年龄会+1,将对象挪回到From区,不断交替循环,随着分代年龄到15,再执行一次minor gc 就会挪到老年代,
固定的线程池经过一系列的轮转,就会在老年代!老年代一旦被放满,java虚拟机就会执行一次full gc!
如下面程序会生成大量HeapTest对象,但是又不能被回收,因为被heapTests引用了!
打开jvisualvm工具
gc日志:如下图
java虚拟机调优的目的就是:减少full gc的次数和full gc(会卡死)一次执行的时间!
上面说的就是元空间满了,导致full gc,元空间参数默认21M,现在调大点即可,一个web应用启动过程中,耗时太久,war包或者jar包太大,在启动的时候,看下日志中的full gc!
如何打印gc日志呢?