一:JDK体系结构
二:JVM整体结构及内存模型
public class Math {
public static final int initData = 666;
public int compute(){
int a = 1;
int b = 2;
int c = (a+b)*10;
return c;
};
public static void main(String[] args){
Math math = new Math();
math.compute();
}
}
当我们执行main方法,jvm会在 线程栈中 分配一块空间。每一个线程独用 栈,程序计数器,本地方法栈,不会共享。
通过程序计数器,虚拟机会一行一行的执行如下命令。
eg:
0: iconst_1 ->将int类型常量1压入操作数栈
1: istore_1 -> 将int类型值存入局部变量1
javap -c Math.class -> Math.txt 对class文件反编译
参照jvm指令手册:http://note.youdao.com/s/MVsjIxcH
直到进入一个新的方法入口,新的栈帧入栈,当前栈帧改变,又或者遇到返回指令或出现异常结束了方法,对应虚拟机出栈。
执行完毕 局部变量销毁 资源释放。
1: 堆,方法区是所有线程共享内存区域。
2: 栈,程序计数器,本地方法栈,线程私有,不会共享
3:方法区是JVM规范。1.8版本用元空间取代了1.7版本及以前的永久代。元数据区和永久代本质上都是方法区的实现。
不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存(也就是说jvm可以使用外边的内存)。 因此,默认情况下,元空间的大小仅受本地内存限制。
元空间存储:已被虚拟机加载的类信息、常量、静态变量、JIT(just-in-time compilation)即时编译器编译后的代码等数据。
运行时常量池 是方法区的一部分 Class文件中除了有类的版本/字段/方法/接口等描述信息外,还有一项信息是Class常量池 (Constant Pool Table),用于存放编译期生成的各种字面量和符号引用(通过 javap -v 类名.class即可反编译查看) 这部分内容将类在加载后进入方法区的运行时常量池中存放。
运行时常量池相对于Class文件常量池(Constant pool:)的另外一个重要特征就是具备动态性当然并非Class文件常量池中 的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。 这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载。
4: 本地方法栈:为虚拟机使用到的Native方法服务。
栈:
用来存放基本数据类型:boolean、char、byte、short、int、long、float、double 引用类型变量时,栈(局部变量表)存储的是指向堆的内存地址(或者说对象引用),真正数据在堆内存中。
栈帧是什么?
栈帧(Stack Frame)是用于虚拟机执行时 方法调用和方法执行时的数据结构,它是虚拟栈(特性FILO)的基本元素。
栈帧包含的内容有:
(1)局部变量表:
在编译时即可确定。存放方法参数和局部变量;它的基本单位为变量槽(slot);它是有索引的,就像数组一样。 从0开始,到表的最大索引,要注意的是,方法参数的个数 + 局部变量的个数 ≠ Slot的数量。因为Slot的空间 是可以复用的,当pc计数器的值已经超出了某个变量的作用域时,下一个变量不必使用新的Slot空间,可以去覆盖 前面那个空间。 虚拟机是通过索引定位的方式使用局部变量表。
(2)操作数栈:
(1)做计算等操作 临时的中转存放 内存空间。
2)栈桢刚创建时,里面的操作数栈是空的。
(3)提供指令来让操作数栈对一些数据进行入栈操作,比如可以把局部变量表里的数据、实例的字段等数据入栈。
public int compute();
Code:
0: iconst_1 ->将int类型常量1压入操作数栈
1: istore_1 -> 将int类型值存入局部变量1
2: iconst_2 3: istore_2
(3) 动态链接:是在程序运行期间完成的将符号引用(Class常量池中的)替换为直接引用。
(4) 方法返回地址:
当执行遇到返回指令,会将返回值传递给上层的方法调用者,这种退出的方式称为正常完成出口 当执行遇到异常,并且当前方法体内没有得到处理,就会导致方法退出,此时是没有返回值的,称为异常完成出口。返回地址要通过异常处理器表来确定。
5: 字符串常量池
JVM为了提高性能和减少内存开销,在实例化字符串常量的时候为字符串开辟一个字符串常量池。
字符串常量池位置:
Jdk1.6及之前: 有永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池 Jdk1.7:有永久代,字符串常量池从永久代里的运行时常量池分离到堆里
Jdk1.8及之后: 无永久代,运行时常量池在元空间(方法区),字符串常量池里依然在堆里
创建字符串常量时,首先查询字符串常量池是否存在该字符串(可类比缓存池) 存在该字符串,返回引用实例,不存在,实例化该字符串并放入池中!
三:JVM内存参数设置
Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里): java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K -XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar microservice‐eurek a‐server.jar
关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N
-XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
-XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M. 达到该值就会触发full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。
由于调整元空间的大小需要Full GC,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生 了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大, 对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。
-Xss 栈线程 设置越小count值越小,说明一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多