认识java虚拟机的基本结构
java虚拟机基本结构:
类加载子系统:负责从文件系统或网络中加载class信息,加载的类信息存放于一块称为方法区的内存空间。除类信息外,方法区中可能还会存放运行时常量池信息,包括字符串常量和数字常量
java堆:虚拟机启动时建立,它是java程序最主要的内存工作区域,几乎所有的Java对象实例都存放于java堆中,堆空间是所有线程共享的,这是一块与java应用密切相关的内存空间。
直接内存:java的NIO库允许java程序使用直接内存,直接内存是java堆外的、直接向系统申请的内存区间。通常,访问直接内存的速度优于java堆,所以读写频繁的场合可以考虑使用java直接内存,
由于直接内存在java堆外,因此它的大小不会直接受限于Xmx制定的最大堆的大小,但是系统内存也是有限的,java堆和直接内存的总和受限于系统能给出的最大内存。
垃圾回收系统:可以对方法区、Java堆、直接内存进行回收,其中java堆是垃圾收集器的重点,全都是隐式的,完成对方法区、Java堆、直接内存全自动化管理。
Java栈:每一个Java虚拟机线程都有一个私有的Java栈。一个线程的java栈在线程创建时候被创建。Java栈中保存帧信息、局部变量、方法参数,同时和java的方法调用和返回密切相关。
本地方法栈:与java栈类似,用于本地方法调用。
PC寄存器(Program Counter):是每个线程私有空间,java虚拟机会为每一个java线程创建pc寄存器。在任意时刻,一个线程总在执行一个方法,这个正在执行的方法为当前方法。如果当前方法不为本地方法,
pc寄存器就会指向当前正在被执行的方法。如果当前方法是本地方法,则pc寄存器的值是undefined。
执行引擎:是java虚拟机最核心的组件之一,它负责执行虚拟机的字节码。现代虚拟机为了提高效率,会使用即时编译技术将方法编译成机器码后再执行。
学会设置Java虚拟机参数:
java [-options] class [args...]
-options:java启动参数
argrs:传递给主函数main()的参数
class:带有main方法的Java类
eg: java -Xmx32m simple a
-Xmx32m参数传给java虚拟机,使得系统堆内存为32mb,参数a传给main方法。
辨清java堆内存:
Java堆是和java应用程序关系最为密切的内存空间,几乎所有的对象都存放在堆中,并且Java堆是完全自动化管理的。
根据垃圾回收机制的不同,Java堆有可能拥有不同的结构。最为常见的一种是将整个java堆分为新生代和老年代,
s0,s1也被称为form和to区域,两块大小相等。
eg:
public class SimpleHeap {
private int id;
public SimpleHeap(int id) {
this.id = id;
}
public void show() {
System.out.println("My Id:" + id);
}
public static void main(String[] args) {
SimpleHeap simpleHeap1 = new SimpleHeap(1);
SimpleHeap simpleHeap2 = new SimpleHeap(2);
simpleHeap1.show();
simpleHeap2.show();
}
}
堆,方法区,java栈分布:
函数如何调用:出入java栈
java栈线程私有的空间,与线程执行密切相关,函数调用都是通过java栈完成的,在java栈中保存主要内容为栈帧。
java方法有两种返回函数的方式:
1.正常的函数返回,使用return指令。
2.抛出异常,
不管方式都会导致栈帧被弹出。
在一个栈帧中,至少包含:局部变量表(保存被调用函数内部的局部变量)、操作数栈(用于计算过程中间的结果)和帧数据区(保存访问常量池的指针,异常处理表)。
java栈上分配,是java虚拟机提供的一种优化技术,基本思路是线程私有的对象(指不能被其他线程访问的对象),可以分配在栈上,好处是在函数调用结束后自行销毁,而不需要垃圾回收器的介入,提高系统的效率。
逃逸分析:
目的是判断对对象的作用域是否有可能逃逸出函数体。
eg:
-server 参数,执行程序,因为在SERVER模式下,才可以启动逃逸分析。
public class aaa1 {
private static Date date = null;
public static void main(String[] args) {
date = new Date(); //对象date是类成员,该字段有可能被其他线程访问,因此属于逃逸对象。
}
}
public class aaa1 {
public static void main(String[] args) {
private static Date date = new Date(); // date 一个非逃逸对象。
}
}
每次函数调用都会占用一定的栈空间,当栈空间不足时,自然不能再调用函数,系统会抛出*Error栈溢出错误。
-Xss 来指定线程的最大栈空间,也决定了函数调用的最大深度。
-XX:+PrintGC 参数,可以看到垃圾回收前后堆的大小变化。
识别方法区:
和java堆一样,方法区是一块所有线程共享的内存区域。用于保存系统类的信息,比如类的字段、方法、常量池等。方法区的大小决定保存多少个类。如果程序定义了太多类,则会导致方法区溢出,虚拟机同样会抛出内存溢出的错误。在jdk1.6和jdk1.7中方法区理解为永久区。
永久区可以使用
参数 -XX:PermSize 和 -XX:MaxPermSize指定。
默认-XX:PermSize 为64MB,一个大方法区可以保存更多的类的信息,如果是用到动态代理,怎需要方法区设置大一点。
eg:
-XX:+PrintGCDetails -XX:PermSize=5M -XX:MaxPermSize=5m
补充: 监控jvm的实时动态信息:可以使用jdk自带的Visual VM进行分析,即${JAVA_HOME}/bin/jvisualvm.exe