所谓虚拟机,就是一台虚拟的机器。他是一款软件,用来执行一系列虚拟计算指令,大体上虚拟机可以分为
系统虚拟机和程序虚拟机, 大名鼎鼎的Visual Box、Vmare就属于系统虚拟机,他们完全是对物理计算的仿真,
提供了一个可以运行完整操作系统的软件平台。
程序虚拟机典型代码就是Java虚拟机,它专门为执行单个计算程序而计算,在Java虚拟机中执行的指令我们成为Java
自己码指令。无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中。
Java发展至今,出现过很多虚拟机,做初Sun使用的一款叫ClassIc的Java虚拟机,到现在引用最广泛的是HotSpot虚拟
机,除了Sum意外,还有BEA的Jrockit,目前Jrockit和HostSopt都被oralce收入旗下,大有整合的趋势。
1、 类加载子系统:负责从文件系统或者网络加载Class信息,加载的信息存放在一块称之方法区的内存空间。
2、 方法区:就是存放类的信息、常量信息、常量池信息、包括字符串字面量和数字常量等。
3、 Java堆:在Java虚拟机启动的时候建立Java堆,它是Java程序最主要的内存工作区域,几乎所有的对象实例都存放到
Java堆中,堆空间是所有线程共享。
4、 直接内存:JavaNio库允许Java程序直接内存,从而提高性能,通常直接内存速度会优于Java堆。读写频繁的场合可能会考虑使用。
5、 每个虚拟机线程都有一个私有栈,一个线程的Java栈在线程创建的时候被创建,Java栈保存着局部变量、方法参数、同事Java的方法调用、
返回值等。
6、 本地方法栈,最大不同为本地方法栈用于本地方法调用。Java虚拟机允许Java直接调用本地方法(通过使用C语言写)
7、 垃圾收集系统是Java的核心,也是不可少的,Java有一套自己进行垃圾清理的机制,开发人员无需手工清理,下一节课详细讲。
8、 PC(Program Couneter)寄存器也是每个线程私有的空间, Java虚拟机会为每个线程创建PC寄存器,在任意时刻,
一个Java线程总是在执行一个方法,这个方法称为当前方法,如果当前方法不是本地方法,PC寄存器总会执行当前正在被执行的指令,
如果是本地方法,则PC寄存器值为Underfined,寄存器存放如果当前执行环境指针、程序技术器、操作栈指针、计算的变量指针等信息。
9、 虚拟机核心的组件就是执行引擎,它负责执行虚拟机的字节码,一般户先进行编译成机器码后执行。
堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名,或者代号。
根据垃圾回收机制的不同,Java堆有可能拥有不同的结构,最为常见的就是将整个Java堆分为
新生代和老年代。其中新声带存放新生的对象或者年龄不大的对象,老年代则存放老年对象。
新生代分为den区、s0区、s1区,s0和s1也被称为from和to区域,他们是两块大小相等并且可以互相角色的空间。
绝大多数情况下,对象首先分配在eden区,在新生代回收后,如果对象还存活,则进入s0或s1区,之后每经过一次
新生代回收,如果对象存活则它的年龄就加1,对象达到一定的年龄后,则进入老年代。
Java栈是一块线程私有的空间,一个栈,一般由三部分组成:局部变量表、操作数据栈和帧数据区
局部变量表:用于报错函数的参数及局部变量
操作数栈:主要保存计算过程的中间结果,同时作为计算过程中的变量临时的存储空间。
帧数据区:除了局部变量表和操作数据栈以外,栈还需要一些数据来支持常量池的解析,这里帧数据区保存着
访问常量池的指针,方便计程序访问常量池,另外当函数返回或出现异常时卖虚拟机子必须有一个异常处理表,方便发送异常
Java方法区和堆一样,方法区是一块所有线程共享的内存区域,他保存系统的类信息。
比如类的字段、方法、常量池等。方法区的大小决定系统可以保存多少个类。如果系统
定义太多的类,导致方法区溢出。虚拟机同样会抛出内存溢出的错误。方法区可以理解
为永久区。
在虚拟机运行的过程中,如果可以跟踪系统的运行状态,那么对于问题的故障
排查会有一定的帮助,为此,在虚拟机提供了一些跟踪系统状态的参数,使用
给定的参数执行Java虚拟机,就可以在系统运行时打印相关日志,用于分析实际
问题。我们进行虚拟机参数配置,其实就是围绕着堆、栈、方法区、进行配置。
你说下 你熟悉那些jvm参数调优
-XX:+PrintGC 每次触发GC的时候打印相关日志
-XX:+UseSerialGC 串行回收
-XX:+PrintGCDetails 更详细的GC日志
-Xms 堆初始值
-Xmx 堆最大可用值
-Xmn 新生代堆最大可用值
-XX:SurvivorRatio 用来设置新生代中eden空间和from/to空间的比例.
含以-XX:SurvivorRatio=eden/from=den/to
总结:在实际工作中,我们可以直接将初始的堆大小与最大堆大小相等,
这样的好处是可以减少程序运行时垃圾回收次数,从而提高效率。
-XX:SurvivorRatio 用来设置新生代中eden空间和from/to空间的比例.
参数: -Xms5m-Xmx20m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:+PrintCommandLineFlags
publicclass JvmDemo01 {
publicstaticvoid main(String[] args) throws InterruptedException { byte[] b1 = newbyte[1 * 1024 * 1024]; System.out.println("分配了1m"); jvmInfo(); Thread.sleep(3000); byte[] b2 = newbyte[4 * 1024 * 1024]; System.out.println("分配了4m"); Thread.sleep(3000); jvmInfo();
} staticprivate String toM(longmaxMemory) { floatnum = (float) maxMemory / (1024 * 1024); DecimalFormat df = new DecimalFormat("0.00");// 格式化小数 String s = df.format(num);// 返回的是String类型 returns; } staticprivatevoid jvmInfo() { // 最大内存 longmaxMemory = Runtime.getRuntime().maxMemory(); System.out.println("maxMemory:" + maxMemory + ",转换为M:" + toM(maxMemory)); // 当前空闲内存 longfreeMemory = Runtime.getRuntime().freeMemory(); System.out.println("freeMemory:" +freeMemory+",转换为M:"+toM(freeMemory)); // 已经使用内存 longtotalMemory = Runtime.getRuntime().totalMemory(); System.out.println("totalMemory:" +totalMemory+",转换为M"+toM(totalMemory)); } }
|
-Xmn 新生代大小,一般设为整个堆的1/3到1/4左右
-XX:SurvivorRatio 设置新生代中eden区和from/to空间的比例关系n/1
参数: -Xms20m-Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC
publicclass JvmDemo02 {
publicstaticvoid main(String[] args) { //-Xms20m -Xmx20m -Xmn1m -XX:SurvivorRatio=2 -XX:+PrintGCDetails -XX:+UseSerialGC byte [] b = null; for (inti = 0; i < 10; i++) { b =newbyte[1*1024*1024]; }
}
}
|
代的比例:-XX:NewRatio=老年代/新生代