JAVA JVM
JVM 虚拟机家族考
http://icyfenix.iteye.com/blog/1133984
http://www.infoq.com/cn/articles/jvm-family
http://blog.sina.com.cn/s/blog_605f5b4f0100t2ps.htmlJAVA JVM / IBM JAVA J9 VM
IBM Support Assiant / IBM ISA
IBM Heap Analyzer / ha.jar
IBM Javacore Analyzer / jca.jar
IBM Garbage Collector Analyzer/ ga.jar
Eclipse Memory Analyzer Tool / Eclipse MAT DTFJ Plugin
jca.jar / jca457.jar
https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=2245aa39-fa5c-4475-b891-14c205f7333c
ha.jar / ha456.jar
https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=4544bafe-c7a2-455f-9d43-eb866ea60091
ga.jar / ga456.jar
https://www.ibm.com/developerworks/community/groups/service/html/communityview?communityUuid=22d56091-3a7b-4497-b36e-634b51838e11
IBM ISA PMAT:java –Xmx[heapsize] –jar ga439.jar
IBM ISA TMDA:Java –Xmx[heapsize] –jar jca457.jar
IBM ISA HeapAnalyzer:java –Xmx[heapsize] –jar ha456.jar
JAVA JVM / Oracle JAVA HotSpot JVM
Eclipse Memory Analyzer Tool / Eclipse MAT
HPJMeter
HPJTune
http://www-01.ibm.com/software/support/isa
http://www.alphaworks.ibm.com/tech/heapanalyzer/download
http://www.alphaworks.ibm.com/tech/jca/download
http://www.alphaworks.ibm.com/tech/pmat/download
http://h20392.www2.hp.com/portal/swdepot/displatProductInfo.do?productNumber=HPJMETER
http://www.eclipse.org/mat/
http://www.ibm.com/developerworks/java/jdk/tools/dtfj.html
http://download.oracle.com/docs/cd/E13150_01/jrockit_jvm/jrockit/tools/index.html
JAVA JVM / Oracle JAVA JRockit VM
BEA JRockit Mission Control
JAVA JVM / HP-UX
Eclipse Memory Analyzer Tool / Eclipse MAT
HPJMeter
HPJTune
JAVA JVM / SAP
Eclipse Memory Analyzer Tool / Eclipse MAT
HPJMeter
HPJTune
JAVA JVM / Borland
JAVA JVM / Hitachi
Sun Classic 虚拟机 / Sun Exact 虚拟机 / Apache Harmony 虚拟机
嵌入式虚拟机
Dalvik 虚拟机
KVM 虚拟机
CDC/CLDC HotSpot 虚拟机(phoneME Advanced/Feature 虚拟机)
其他虚拟机
http://icyfenix.iteye.com/blog/1133984
http://www.infoq.com/cn/articles/jvm-family
http://blog.sina.com.cn/s/blog_605f5b4f0100t2ps.html
JavaInJava
http://labs.oracle.com/kanban/JavaInJava.html
Maxine
JamVM:http://jamvm.sourceforge.net/
cacaovm:http://www.cacaovm.org/
SableVM:http://www.sablevm.org/
Kaffe:http://www.kaffe.org/
Jelatine JVM:http://jelatine.sourceforge.net/
NanoVM:http://www.harbaum.org/till/nanovm/index.shtml
MRP:http://mrp.codehaus.org/
Moxie JVM:http://moxie.sourceforge.net/
Jikes RVM:http://jikesrvm.org/
JAVA JVM jvmtop
Java monitoring for the command-line
http://jvmtop.googlecode.com/files/jvmtop-0.6.0.tar.gz
http://dl.iteye.com/topics/download/a3eb4565-4aea-3c55-b109-2ddbdb9c9d16
[root@localhost Desktop]# jar -tvf jvmtop.jar
25 Wed Jun 05 15:19:40 CST 2013 META-INF/MANIFEST.MF
4632 Wed Jun 05 14:02:32 CST 2013 com/jvmtop/JvmTop.class
5215 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/VMOverviewView.class
215 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/ConsoleView.class
7548 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/VMDetailView.class
965 Wed Jun 05 12:24:56 CST 2013 com/jvmtop/AbstractConsoleView$1.class
3400 Wed Jun 05 12:24:56 CST 2013 com/jvmtop/AbstractConsoleView.class
1282 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/VMInfoState.class
1026 Wed Jun 05 13:53:02 CST 2013 com/jvmtop/VMInfo$CPULoadComparator.class
1025 Wed Jun 05 13:53:02 CST 2013 com/jvmtop/VMInfo$UsedHeapComparator.class
12296 Wed Jun 05 13:53:02 CST 2013 com/jvmtop/VMInfo.class
1775 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/MemoryPoolStat.class
1362 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/JConsoleContext$ConnectionState.class
637 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/JConsoleContext.class
1256 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/ProxyClient$Snapshot.class
798 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/ProxyClient$SnapshotInvocationHandler$NameValueMap.class
5406 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/ProxyClient$SnapshotInvocationHandler.class
331 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/ProxyClient$SnapshotMBeanServerConnection.class
1272 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/ProxyClient$WeakPCL.class
24733 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/ProxyClient.class
2233 Wed Jun 05 12:24:54 CST 2013 com/jvmtop/openjdk/tools/JConsolePlugin.class
9734 Wed Jun 05 12:30:40 CST 2013 com/jvmtop/openjdk/tools/LocalVirtualMachine.class
[root@localhost Desktop]# more jvmtop.sh
#!/bin/bash DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" $JAVA_HOME/bin/java $JAVA_OPTS -cp $DIR/jvmtop.jar:$JAVA_HOME/lib/tools.jar com.jvmtop.JvmTop $1
[root@localhost Desktop]# chmod 777 jvmtop.sh jvmtop.jar
[root@localhost Desktop]# ./jvmtop.sh
JvmTop 0.6.0 alpha i386, 2 cpus, Linux 2.6.18-19
http://code.google.com/p/jvmtop
PID MAIN-CLASS HPCUR HPMAX NHCUR NHMAX CPU GC VM USERNAME #T DL
30413 m.jvmtop.JvmTop 2m 247m 17m 118m 60.61% 0.00% S6U43 root 14
深入java虚拟机.pdf P287 / 第11章 晚期(运行期)优化 / JVM配置参数中文说明
https://java.net/projects/c1visualizer/
https://wikis.oracle.com/display/MaxineVM/C1X
https://java.net/downloads/c1visualizer/c1visualizer.zip
JVM配置参数中文说明
http://luckykapok918.blog.163.com/blog/static/20586504320121016113658768/
1、-Xmixed mixed mode execution (default)
混合模式执行
2、-Xint interpreted mode execution only
解释模式执行
曹静/(IBM-C) : 代码运行到一定次数后会编译本地代码,比解释执行效率高
3、-Xbootclasspath:
set search path for bootstrap classes and resources
设置zip/jar资源或者类(.class文件)存放目录路径
3、-Xbootclasspath/a:
append to end of bootstrap class path
追加zip/jar资源或者类(.class文件)存放目录路径
4、-Xbootclasspath/p:
prepend in front of bootstrap class path
预先加载zip/jar资源或者类(.class文件)存放目录路径
5、-Xnoclassgc disable class garbage collection
关闭类垃圾回收功能
6、-Xincgc enable incremental garbage collection
开启类的垃圾回收功能
7、-Xloggc:
记录垃圾回日志到一个文件。
8、-Xbatch disable background compilation
关闭后台编译
9、-Xms
设置JVM初始化堆内存大小
10、-Xmx
设置JVM最大的堆内存大小
11、-Xss
设置JVM栈内存大小
12、-Xprof output cpu profiling data
输入CPU概要表数据
13、-Xfuture enable strictest checks, anticipating future default
执行严格的代码检查,预测可能出现的情况
14、-Xrs reduce use of OS signals by Java/VM (see documentation)
通过JVM还原操作系统信号
15、-Xcheck:jni perform additional checks for JNI functions
对JNI函数执行检查
16、-Xshare:off do not attempt to use shared class data
尽可能不去使用共享类的数据
17、-Xshare:auto use shared class data if possible (default)
尽可能的使用共享类的数据
18、-Xshare:on require using shared class data, otherwise fail.
尽可能的使用共享类的数据,否则运行失败
常见JVM参数配置汇总
堆设置
-Xms:初始堆大小(Heap )
-Xmx:最大堆大小(Heap )
-XX:NewSize=n:设置年轻代大小(指的是 NEW Generation)
-XX:NewRatio=n:设置年轻代和年老代(Old Generation)的比值。
如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代(NEW Generation)的1/5
-XX:MaxPermSize=n:设置持久代大小(内存的永久保存区域)
PermGen space的全称是Permanent Generation space,是指内存的永久保存区域
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。
深入java虚拟机.pdf P90
http://jjidc.jb51.net:81/201211/books/srljjavaxnj_jvmgjtxyzjsz_jb51.rar
Java的内存泄漏
欧阳辰 ([email protected]),
周欣 (mailto:[email protected]),
http://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/index.html
简介:
Java的一个重要优点就是通过垃圾收集器(Garbage Collection,GC)自动管理内存的回收,程序员不需要通过调用函数来释放内存。
因此,很多程序员认为Java不存在内存泄漏问题,或者认为即使有内存泄漏也不是程序的责任,而是GC或JVM的问题。
其实,这种想法是不正确的,因为Java也存在内存泄露,但它的表现与C++不同。
OOMObject.java
package com.iteye.lindows;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
/**
*
* @author Lindows
* @see 内存占位符对象,一个OOMObject 大约占6.25MB
* 《深入理解java虚拟机》P90
* http://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/index.html
*
*/
public class OOMObject {
public byte[] placeholder = new byte[6400 * 1024];
public static void fillHeap(int num) throws InterruptedException {
List list = new ArrayList();
for (int i = 0; i < num; i++) {
// 稍作延时,令监视曲线的变化更加明显
Thread.sleep(50);
list.add(new OOMObject());
System.out.println("run 1000次");
}
// System.gc();
System.out.println("gc over");
}
/*
public static void fillHeap2(int num) throws InterruptedException {
Vector
运行日志如下:
run 1000次 run 1000次 、、、 run 1000次 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.iteye.lindows.OOMObject.(OOMObject.java:14) at com.iteye.lindows.OOMObject.fillHeap(OOMObject.java:21) at com.iteye.lindows.OOMObject.main(OOMObject.java:39)
JDK
http://openjdk.java.net/
JDK TOOLS——jps、jinfo、jstat、jmap、jhat、 jstack
http://whythiszhao.iteye.com/blog/492176
JDK TOOLS—— jconsole.exe、jvisualvm.exe
D:\Program Files\Java\jdk1.6.0_10\bin
Eclipse Memory Analyzer Tool (MAT) 适用于HP-UX、SAP、HotSpot VM,安装IBM DTFJ 插件后可支持IBM J9 VM
OracleJRockit Management Control
http://baike.baidu.com/view/1792470.htm
BEA JRockit Mission Control 适用于JRockit VM
图解JVM在内存中申请对象及垃圾回收流程
http://longdick.iteye.com/blog/468368
JVM内存最大能调多大
http://www.iteye.com/topic/80257?page=1
可以设置的最大JVM内存和JVM版本以及操作系统版本有关,
一般Windows下1200-1500M左右,Linux下最大能到2600M;
具体可以使用命令 java -XmxXXXXM -version 来进行测试(如:java -Xmx1024M -version),
然后逐渐的增大XXXX的值,如果执行正常就表示指定的内存大小可用,否则会打印错误信息。
Garbage Collection and Storage Allocation techniques.rar
http://www.iteye.com/topics/download/36f9fbd4-d797-40cb-a527-304556587717
http://zwchen.iteye.com/blog/74737
IBM WebSphere Application Server 诊断和调优(一)
D:\ibm_sdk60
https://www14.software.ibm.com/webapp/iwm/web/reg/download.do?source=swg-sdk6&S_PKG=intel_6sr5&S_TACT=105AGX05&S_CMP=JDK&lang=en_US&cp=UTF-8
C:\Documents and Settings\Lindows>java -Xmx1800M -version
java version "1.6.0"
Java(TM) SE Runtime Environment (build pwi3260sr1-20080416_01(SR1))
IBM J9 VM (build 2.4, J2RE 1.6.0 IBM J9 2.4 Windows XP x86-32 jvmwi3260-2008041
_18762 (JIT enabled, AOT enabled)
J9VM - 20080415_018762_lHdSMr
JIT - r9_20080415_1520
GC - 20080415_AA)
JCL - 20080412_01
JDK jconsole 控制台
Jconsole,Java Monitoring and Management Console。
java监控和管理控制台,从java5开始,在JDK中提供。
用于对JVM中内存,线程和类等的监控。
本文使用java6,SUN JDK1.6.0_03,使用JDK1.5版本使用略有不同。
本文使用windows XP。
确认jdk的bin目录设置到环境变量Path中。
在命令行中输入:jconsole
如果弹出窗口,说明配置可用。
这里使用的是本地JVM监控,如果要监控远程的JVM需要另外的配置。
首先,启动需要监控的Java应用程序。
通过任务管理器的进程标签,查看该进程的PID,比如是1388
在命令行启动jconsole: jconsole 1388
在启动的界面中:
概述:有关堆内存使用情况,线程,类加载和CPU使用情况的综述;
- 内存:内存的详细情况,堆和其他内存;
- 线程:峰值/活动线程,另外,各个线程的明细信息,检测死锁;
- 类:监控加载和卸载的类;
- vm摘要:有关vm的明细信息
- MBean:当前Java程序的MBean(如果有的话)的操作。
JDK jrunscript 脚本
安装JDK6以后,如果将JDK6的bin目录设置到Path变量中(windows下),那么打开命令行窗口,可以运行如下程序:jrunscript
jrunscript是什么呢,是java下的命令行脚本工具,command line script shell。
jrunscript是java6新增的工具,详细文档,参考Java6 Doc:/docs/technotes/tools/share/jrunscript.html
默认情况下,该工具解释执行javascript脚本。
执行的方式有两种,交互的方式和批处理方式。
该工具也可以解释执行其他脚本语言,如果配置了该语言的解释引擎,比如groovy,ruby,beanshell等。
交互式使用
- D:\>jrunscript
- js> var time= new Date();
- js> println(time);
- Sun Nov 11 2007 16:43:37 GMT+0800 (CST)
- js> println(java.text.SimpleDateFormat('yyyy-MM-dd').format(time));
- 2007-11-11
- js>
为什么没有使用alert()?那是浏览器中才有的对象方法(window.alert()),这里只支持javascript的核心语法和功能,不过,可以通过这里的javascript直接访问java对象。
退出交互环境:
- exit();
批处理使用
创建js文件:
- var time= new Date();
- println(java.text.SimpleDateFormat('yyyy-MM-dd').format(time));
运行:
- E:\>jrunscript sample.js
- 2007 - 11 - 11
JVM
http://open.iteye.com/blog/179513
JVM 上的数据类型
一、什么是Java虚拟机
当你谈到Java虚拟机时,你可能是指:
1、抽象的Java虚拟机规范
2、一个具体的Java虚拟机实现
3、一个运行的Java虚拟机实例
二、Java虚拟机的生命周期
一个运行中的Java虚拟机有着一个清晰的任务:执行Java程序。程序开始执行时他才运行,程序结束时他就停止。你在同一台机器上运行三个程序,就会有三个运行中的Java虚拟机。
Java虚拟机总是开始于一个main()方法,这个方法必须是公有、返回void、直接受一个字符串数组。在程序执行时,你必须给Java虚拟机指明这个包换main()方法的类名。
Main()方法是程序的起点,他被执行的线程初始化为程序的初始线程。程序中其他的线程都由他来启动。Java中的线程分为两种:守护线程 (daemon)和普通线程(non-daemon)。守护线程是Java虚拟机自己使用的线程,比如负责垃圾收集的线程就是一个守护线程。当然,你也可 以把自己的程序设置为守护线程。包含Main()方法的初始线程不是守护线程。
只要Java虚拟机中还有普通的线程在执行,Java虚拟机就不会停止。如果有足够的权限,你可以调用exit()方法终止程序。
三、Java虚拟机的体系结构
在Java虚拟机的规范中定义了一系列的子系统、内存区域、数据类型和使用指南。这些组件构成了Java虚拟机的内部结构,他们不仅仅为Java虚拟机的实现提供了清晰的内部结构,更是严格规定了Java虚拟机实现的外部行为。
每一个Java虚拟机都由一个类加载器子系统(class loader subsystem),负责加载程序中的类型(类和接口),并赋予唯一的名字。每一个Java虚拟机都有一个执行引擎(execution engine)负责执行被加载类中包含的指令。
程序的执行需要一定的内存空间,如字节码、被加载类的其他额外信息、程序中的对象、方法的参数、返回值、本地变量、处理的中间变量等等。Java虚拟机将 这些信息统统保存在数据区(data areas)中。虽然每个Java虚拟机的实现中都包含数据区,但是Java虚拟机规范对数据区的规定却非常的抽象。许多结构上的细节部分都留给了 Java虚拟机实现者自己发挥。不同Java虚拟机实现上的内存结构千差万别。一部分实现可能占用很多内存,而其他以下可能只占用很少的内存;一些实现可 能会使用虚拟内存,而其他的则不使用。这种比较精炼的Java虚拟机内存规约,可以使得Java虚拟机可以在广泛的平台上被实现。
数据区中的一部分是整个程序共有,其他部分被单独的线程控制。每一个Java虚拟机都包含方法区(method area)和堆(heap),他们都被整个程序共享。Java虚拟机加载并解析一个类以后,将从类文件中解析出来的信息保存与方法区中。程序执行时创建的 对象都保存在堆中。
当一个线程被创建时,会被分配只属于他自己的PC寄存器“pc register”(程序计数器)和Java堆栈(Java stack)。当线程不掉用本地方法时,PC寄存器中保存线程执行的下一条指令。Java堆栈保存了一个线程调用方法时的状态,包括本地变量、调用方法的 参数、返回值、处理的中间变量。调用本地方法时的状态保存在本地方法堆栈中(native method stacks),可能再寄存器或者其他非平台独立的内存中。
Java堆栈有堆栈块(stack frames (or frames))组成。堆栈块包含Java方法调用的状态。当一个线程调用一个方法时,Java虚拟机会将一个新的块压到Java堆栈中,当这个方法运行结束时,Java虚拟机会将对应的块弹出并抛弃。
Java虚拟机不使用寄存器保存计算的中间结果,而是用Java堆栈在存放中间结果。这是的Java虚拟机的指令更紧凑,也更容易在一个没有寄存器的设备上实现Java虚拟机。
图中的Java堆栈中向下增长的,PC寄存器中线程三为灰色,是因为它正在执行本地方法,他的下一条执行指令不保存在PC寄存器中。
四、数据类型(Data Types)
所有Java虚拟机中使用的数据都有确定的数据类型,数据类型和操作都在Java虚拟机规范中严格定义。Java中的数据类型分为原始数据类型 (primitive types)和引用数据类型(reference type)。引用类型依赖于实际的对象,但不是对象本身。原始数据类型不依赖于任何东西,他们就是本身表示的数据。
所有Java程序语言中的原始 数据类型,都是Java虚拟机的原始数据类型,除了布尔型(boolean)。当编译器将Java源代码编译为自己码时,使用整型(int)或者字节型 (byte)去表示布尔型。在Java虚拟机中使用整数0表示布尔型的false,使用非零整数表示布尔型的true,布尔数组被表示为字节数组,虽然他 们可能会以字节数组或者字节块(bit fields)保存在堆中。
除了布尔型,其他Java语言中的原始类型都是Java虚拟机中的数据类型。在Java中数据类型被分为:整形的byte,short,int,long;char和浮点型的float,double。Java语言中的数据类型在任何主机上都有同样的范围。
在Java虚拟机中还存在一个Java语言中不能使用的原始数据类型返回值类型(returnValue)。这种类型被用来实现Java程序中的“finally clauses”,具体的参见18章的“Finally Clauses”。
引用类型可能被创建为:类类型(class type),接口类型(interface type),数组类型(array type)。他们都引用被动态创建的对象。当引用类型引用null时,说明没有引用任何对象。
Java虚拟机规范只定义了每一种数据类型表示的范围,没有定义在存储时每种类型占用的空间。他们如何存储由Java虚拟机的实现者自己决定。关于浮点型更多信息参见14章“Floating Point Arithmetic”。
TypeRange
byte8-bit signed two's complement integer (-27 to 27 - 1, inclusive)
short16-bit signed two's complement integer (-215 to 215 - 1, inclusive)
int32-bit signed two's complement integer (-231 to 231 - 1, inclusive)
long64-bit signed two's complement integer (-263 to 263 - 1, inclusive)
char16-bit unsigned Unicode character (0 to 216 - 1, inclusive)
float32-bit IEEE 754 single-precision float
double64-bit IEEE 754 double-precision float
returnValueaddress of an opcode within the same method
referencereference to an object on the heap, or null
Java虚拟机中最小的数据单元式字(word),其大小由Java虚拟机的实现者定义。但是一个字的大小必须足够容纳byte,short,int, char,float,returnValue,reference;两个字必须足够容纳long,double。所以虚拟机的实现者至少提供的字不能小 于31bits的字,但是最好选择特定平台上最有效率的字长。
在运行时,Java程序不能决定所运行机器的字长。字长也不会影响程序的行为,他只是在Java虚拟机中的一种表现方式。
Java虚拟机中的类加载器分为两种:原始类加载器(primordial class loader)和类加载器对象(class loader objects)。原始类加载器是Java虚拟机实现的一部分,类加载器对象是运行中的程序的一部分。不同类加载器加载的类被不同的命名空间所分割。
类加载器调用了许多Java虚拟机中其他的部分和java.lang包中的很多类。比如,类加载对象就是java.lang.ClassLoader子类 的实例,ClassLoader类中的方法可以访问虚拟机中的类加载机制;每一个被Java虚拟机加载的类都会被表示为一个 java.lang.Class类的实例。像其他对象一样,类加载器对象和Class对象都保存在堆中,被加载的信息被保存在方法区中。
1、加载、连接、初始化(Loading, Linking and Initialization)
类加载子系统不仅仅负责定位并加载类文件,他按照以下严格的步骤作了很多其他的事情:(具体的信息参见第七章的“类的生命周期”)
1)、加载:寻找并导入指定类型(类和接口)的二进制信息
2)、连接:进行验证、准备和解析
①验证:确保导入类型的正确性
②准备:为类型分配内存并初始化为默认值
③解析:将字符引用解析为直接饮用
3)、初始化:调用Java代码,初始化类变量为合适的值
2、原始类加载器(The Primordial Class Loader)
每个Java虚拟机都必须实现一个原始类加载器,他能够加载那些遵守类文件格式并且被信任的类。但是,Java虚拟机的规范并没有定义如何加载类,这由 Java虚拟机实现者自己决定。对于给定类型名的类型,原始莱加载器必须找到那个类型名加“.class”的文件并加载入虚拟机中。
3、类加载器对象
虽然类加载器对象是Java程序的一部分,但是ClassLoader类中的三个方法可以访问Java虚拟机中的类加载子系统。
1)、protected final Class defineClass(…):使用这个方法可以出入一个字节数组,定义一个新的类型。
2)、protected Class findSystemClass(String name):加载指定的类,如果已经加载,就直接返回。
3)、protected final void resolveClass(Class c):defineClass()方法只是加载一个类,这个方法负责后续的动态连接和初始化。
具体的信息,参见第八章“连接模型”( The Linking Model)。
4、命名空间
当多个类加载器加载了同一个类时,为了保证他们名字的唯一性,需要在类名前加上加载该类的类加载器的标识。具体的信息,参见第八章“连接模型”( The Linking Model)。
七、方法区(The Method Area)
在Java虚拟机中,被加载类型的信息都保存在方法区中。这写信息在内存中的组织形式由虚拟机的实现者定义,比如,虚拟机工作在一个“little- endian”的处理器上,他就可以将信息保存为“little-endian”格式的,虽然在Java类文件中他们是以“big-endian”格式保 存的。设计者可以用最适合并地机器的表示格式来存储数据,以保证程序能够以最快的速度执行。但是,在一个只有很小内存的设备上,虚拟机的实现者就不会占用 很大的内存。
程序中的所有线程共享一个方法区,所以访问方法区信息的方法必须是线程安全的。如果你有两个线程都去加载一个叫Lava的类,那只能由一个线程被容许去加载这个类,另一个必须等待。
在程序运行时,方法区的大小是可变的,程序在运行时可以扩展。有些Java虚拟机的实现也可以通过参数也订制方法区的初始大小,最小值和最大值。
方法区也可以被垃圾收集。因为程序中的内由类加载器动态加载,所有类可能变成没有被引用(unreferenced)的状态。当类变成这种状态时,他就可 能被垃圾收集掉。没有加载的类包括两种状态,一种是真正的没有加载,另一个种是“unreferenced”的状态。详细信息参见第七章的类的生命周期 (The Lifetime of a Class)。
1、类型信息(Type Information)
每一个被加载的类型,在Java虚拟机中都会在方法区中保存如下信息:
1)、类型的全名(The fully qualified name of the type)
2)、类型的父类型的全名(除非没有父类型,或者弗雷形式java.lang.Object)(The fully qualified name of the typeís direct superclass)
3)、给类型是一个类还是接口(class or an interface)(Whether or not the type is a class )
4)、类型的修饰符(public,private,protected,static,final,volatile,transient等)(The typeís modifiers)
5)、所有父接口全名的列表(An ordered list of the fully qualified names of any direct superinterfaces)
类型全名保存的数据结构由虚拟机实现者定义。除此之外,Java虚拟机还要为每个类型保存如下信息:
1)、类型的常量池(The constant pool for the type)
2)、类型字段的信息(Field information)
3)、类型方法的信息(Method information)
4)、所有的静态类变量(非常量)信息(All class (static) variables declared in the type, except constants)
5)、一个指向类加载器的引用(A reference to class ClassLoader)
6)、一个指向Class类的引用(A reference to class Class)
1)、类型的常量池(The constant pool for the type)
常量池中保存中所有类型是用的有序的常量集合,包含直接常量(literals)如字符串、整数、浮点数的常量,和对类型、字段、方法的符号引用。常量池 中每一个保存的常量都有一个索引,就像数组中的字段一样。因为常量池中保存中所有类型使用到的类型、字段、方法的字符引用,所以它也是动态连接的主要对 象。详细信息参见第六章“The Java Class File”。
2)、类型字段的信息(Field information)
字段名、字段类型、字段的修饰符(public,private,protected,static,final,volatile,transient等)、字段在类中定义的顺序。
3)、类型方法的信息(Method information)
方法名、方法的返回值类型(或者是void)、方法参数的个数、类型和他们的顺序、字段的修饰符(public,private,protected,static,final,volatile,transient等)、方法在类中定义的顺序
如果不是抽象和本地本法还需要保存
方法的字节码、方法的操作数堆栈的大小和本地变量区的大小(稍候有详细信息)、异常列表(详细信息参见第十七章“Exceptions”。)
4)、类(静态)变量(Class Variables)
类变量被所有类的实例共享,即使不通过类的实例也可以访问。这些变量绑定在类上(而不是类的实例上),所以他们是类的逻辑数据的一部分。在Java虚拟机使用这个类之前就需要为类变量(non-final)分配内存
常量(final)的处理方式于这种类变量(non-final)不一样。每一个类型在用到一个常量的时候,都会复制一份到自己的常量池中。常量也像类变 量一样保存在方法区中,只不过他保存在常量池中。(可能是,类变量被所有实例共享,而常量池是每个实例独有的)。Non-final类变量保存为定义他的 类型数据(data for the type that declares them)的一部分,而final常量保存为使用他的类型数据(data for any type that uses them)的一部分。详情参见第六章“The Java Class FileThe Java Class File”
5)、指向类加载器的引用(A reference to class ClassLoader)
每一个被Java虚拟机加载的类型,虚拟机必须保存这个类型是否由原始类加载器或者类加载器加载。那些被类加载器加载的类型必须保存一个指向类加载器的引 用。当类加载器动态连接时,会使用这条信息。当一个类引用另一个类时,虚拟机必须保存那个被引用的类型是被同一个类加载器加载的,这也是虚拟机维护不同命 名空间的过程。详情参见第八章“The Linking Model”
6)、指向Class类的引用(A reference to class Class)
Java虚拟机为每一个加载的类型创建一个java.lang.Class类的实例。你也可以通过Class类的方法:
public static Class forName(String className)来查找或者加载一个类,并取得相应的Class类的实例。通过这个Class类的实例,我们可以访问Java虚拟机方法区中的信息。具体参照Class类的JavaDoc。
2、方法列表(Method Tables)
为了更有效的访问所有保存在方法区中的数据,这些数据的存储结构必须经过仔细的设计。所有方法区中,除了保存了上边的那些原始信息外,还有一个为了加快存 取速度而设计的数据结构,比如方法列表。每一个被加载的非抽象类,Java虚拟机都会为他们产生一个方法列表,这个列表中保存了这个类可能调用的所有实例 方法的引用,报错那些父类中调用的方法。详情参见第八章“The Linking Model”
八、堆
当Java程序创建一个类的实例或者数组时,都在堆中为新的对象分配内存。虚拟机中只有一个堆,所有的线程都共享他。
1、垃圾收集(Garbage Collection)
垃圾收集是释放没有被引用的对象的主要方法。它也可能会为了减少堆的碎片,而移动对象。在Java虚拟机的规范中没有严格定义垃圾收集,只是定义一个Java虚拟机的实现必须通过某种方式管理自己的堆。详情参见第九章“Garbage Collection”。
2、对象存储结构(Object Representation)
Java虚拟机的规范中没有定义对象怎样在堆中存储。每一个对象主要存储的是他的类和父类中定义的对象变量。对于给定的对象的引用,虚拟机必须嫩耨很快的 定位到这个对象的数据。另为,必须提供一种通过对象的引用方法对象数据的方法,比如方法区中的对象的引用,所以一个对象保存的数据中往往含有一个某种形式 指向方法区的指针。
一个可能的堆的设计是将堆分为两个部分:引用池和对象池。一个对象的引用就是指向引用池的本地指针。每一个引用池中的条目都包含两个部分:指向对象池中对 象数据的指针和方法区中对象类数据的指针。这种设计能够方便Java虚拟机堆碎片的整理。当虚拟机在对象池中移动一个对象的时候,只需要修改对应引用池中 的指针地址。但是每次访问对象的数据都需要处理两次指针。下图演示了这种堆的设计。在第九章的“垃圾收集”中的HeapOfFish Applet演示了这种设计。
另一种堆的设计是:一个对象的引用就是一个指向一堆数据和指向相应对象的偏移指针。这种设计方便了对象的访问,可是对象的移动要变的异常复杂。下图演示了这种设计
当程序试图将一个对象转换为另一种类型时,虚拟机需要判断这种转换是否是这个对象的类型,或者是他的父类型。当程序适用instanceof语句的时候也 会做类似的事情。当程序调用一个对象的方法时,虚拟机需要进行动态绑定,他必须判断调用哪一个类型的方法。这也需要做上面的判断。
无论虚拟机实现者使用哪一种设计,他都可能为每一个对象保存一个类似方法列表的信息。因为他可以提升对象方法调用的速度,对提升虚拟机的性能非常重要,但 是虚拟机的规范中比没有要求必须实现类似的数据结构。下图描述了这种结构。图中显示了一个对象引用相关联的所有的数据结构,包括:
1)、一个指向类型数据的指针
2)、一个对象的方法列表。方法列表是一个指向所有可能被调用对象方法的指针数组。方法数据包括三个部分:操作码堆栈的大小和方法堆栈的本地变量区;方法的字节码;异常列表。
每一个Java虚拟机中的对象必须关联一个用于同步多线程的lock(mutex)。同一时刻,只能有一个对象拥有这个对象的锁。当一个拥有这个这个对象 的锁,他就可以多次申请这个锁,但是也必须释放相应次数的锁才能真正释放这个对象锁。很多对象在整个生命周期中都不会被锁,所以这个信息只有在需要时才需 要添加。很多Java虚拟机的实现都没有在对象的数据中包含“锁定数据”,只是在需要时才生成相应的数据。除了实现对象的锁定,每一个对象还逻辑关联到一 个“wait set”的实现。锁定帮组线程独立处理共享的数据,不需要妨碍其他的线程。“wait set”帮组线程协作完成同一个目标。“wait set”往往通过Object类的wait()和notify()方法来实现。
垃圾收集也需要堆中的对象是否被关联的信息。Java虚拟机规范中指出垃圾收集一个运行一个对象的finalizer方法一次,但是容许 finalizer方法重新引用这个对象,当这个对象再次不被引用时,就不需要再次调用finalize方法。所以虚拟机也需要保存finalize方法 是否运行过的信息。更多信息参见第九章的“垃圾收集”
3、数组的保存(Array Representation)
在Java 中,数组是一种完全意义上的对象,他和对象一样保存在堆中、有一个指向Class类实例的引用。所有同一维度和类型的数组拥有同样的Class,数组的长 度不做考虑。对应Class的名字表示为维度和类型。比如一个整型数据的Class为“[I”,字节型三维数组Class名为“[[[B”,两维对象数据 Class名为“[[Ljava.lang.Object”。
多维数组被表示为数组的数组,如下图:
数组必须在堆中保存数组的长度,数组的数据和一些对象数组类型数据的引用。通过一个数组引用的,虚拟机应该能够取得一个数组的长度,通过索引能够访问特定 的数据,能够调用Object定义的方法。Object是所有数据类的直接父类。更多信息参见第六章“类文件”。
九、PC寄存器(程序计数器)(The Program Counter)
每一个线程开始执行时都会被创建一个程序计数器。程序计数器只有一个字长(word),所以它能够保存一个本地指针和returnValue。当线程执行 时,程序计数器中存放了正在执行指令的地址,这个地址可以使一个本地指针,也可以使一个从方法字节码开始的偏移指针。如果执行本地方法,程序计数器的值没 有被定义。
十、Java堆栈(The Java Stack)
当一个线程启动时,Java虚拟机会为他创建一个Java堆栈。Java堆栈用一些离散的frame类纪录线程的状态。Java虚拟机堆Java堆栈的操作只有两种:压入和弹出frames。
线程中正在执行的方法被称为当前方法(current method),当前方法所对应的frame被称为当前帧(current frame)。定义当前方法的类被称为当前类(current class),当前类的常量池被称为当前常量池(current constant pool.)。当线程执行时,Java虚拟机会跟踪当前类和当前常量池。但线程操作保存在帧中的数据时,他只操作当前帧的数据。
当线程调用一个方法时,虚拟机会生成一个新的帧,并压入线程的Java堆栈。这个新的帧变成当前帧。当方法执行时,他使用当前帧保存方法的参数、本地变 量、中间结构和其他数据。方法有两种退出方式:正常退出和异常推出。无论方法以哪一种方式推出,Java虚拟机都会弹出并丢弃方法的帧,上一个方法的帧变 为当前帧。
所有保存在帧中的数据都只能被拥有它的线程访问,线程不能访问其他线程的堆栈中的数据。所以,访问方法的本地变量时,不需要考虑多线程同步。
和方法区、堆一样,Java堆栈不需要连续的内存空间,它可以被保存在一个分散的内存空间或者堆上。堆栈具体的数据和长度都有Java虚拟机的实现者自己定义。一些实现可能提供了执行堆栈最大值和最小值的方法。
十一、堆栈帧(The Stack Frame)
堆栈帧包含三部分:本地变量、操作数堆栈和帧数据。本地变量和操作数堆栈的大小都是一字(word)为单位的,他们在编译就已经确定。帧数据的大小取决于 不同的实现。当程序调用一个方法时,虚拟机从类数据中取得本地变量和操作数堆栈的大小,创建一个合适大小和帧,然后压入Java堆栈中。
1、本地变量(Local Variables)
本地变量在Java堆栈帧中被组织为一个从0计数的数组,指令通过提供他们的索引从本地变量区中取得相应的值。Int,float,reference, returnValue占一个字,byte,short,char被转换成int然后存储,long和doubel占两个字。
指令通过提供两个字索引中的前一个来取得long,doubel的值。比如一个long的值存储在索引3,4上,指令就可以通过3来取得这个long类型的值。
本地变量区中包含了方法的参数和本地变量。编译器将方法的参数以他们申明的顺序放在数组的前面。但是编译器却可以将本地变量任意排列在本地变量数组中,甚至两个本地变量可以公用一个地址,比如,当两个本地变量在两个不交叠的区域内,就像循环变量i,j。
虚拟机的实现者可以使用任何结构来描述本地变量区中的数据,虚拟机规范中没有定义如何存储long和doubel。
2、操作数堆栈(Operand Stack)
向本地变量一样,操作数堆栈也被组织为一个以字为单位的数组。但是不像本地变量那样通过索引访问,而是通过push和pop值来实现访问的。如果一个指令push一个值到堆栈中,那么下一个指令就可以pop并且使用这个值。
操作数堆栈不像程序计数器那样不可以被指令直接访问,指令可以直接访问操作数堆栈。Java虚拟机是一个以堆栈为基础,而不是以寄存器为基础的,因为它的 指令从堆栈中取得操作数,而不是同寄存器中。当然,指令也可以从其他地方去的操作数,比如指令后面的操作码,或者常量池。但是Java虚拟机指令主要是从 操作数堆栈中取得他们需要的操作数。
Java虚拟机将操作数堆栈视为工作区,很多指令通过先从操作数堆栈中pop值,在处理完以后再将结果push回操作数堆栈。一个add的指令执行过程如 下图所示:先执行iload_0和iload_1两条指令将需要相加的两个数,从本地方法区中取出,并push到操作数堆栈中;然后执行iadd指令,现 pop出两个值,相加,并将结果pusp进操作数堆栈中;最后执行istore_2指令,pop出结果,赋值到本地方法区中。
3、帧数据(Frame Data)
处理本地变量和操作数堆栈以外,java堆栈帧还包括了为了支持常量池,方法返回值和异常分发需要的数据,他们被保存在帧数据中。
当虚拟机遇到使用指向常量池引用的指令时,就会通过帧数据中指向常量区的指针来访问所需要的信息。前面提到过,常量区中的引用在最开始时都是符号引用。即使当虚拟机检查这些引用时,他们也是字符引用。所以虚拟机需要在这时转换这个引用。
当一个方法正常返回时,虚拟机需要重建那个调用这个方法的方法的堆栈帧。如果执行完的方法有返回值,虚拟机就需要将这个值push进调用方法的哪个操作数堆栈中。
帧数据中也包含虚拟机用来处理异常的异常表的引用。异常表定义了一个被catch语句保护的一段字节码。每一个异常表中的个体又包含了需要保护的字节玛的 范围,和异常被捕捉到时需要执行的字节码的位置。当一个方法抛出一个异常时,Java虚拟机就是用异常表去判断如何处理这个异常。如果虚拟机找到了一个匹 配的catch,他就会将控制权交给catch语句。如果没有找到匹配的catch,方法就会异常返回,然后再调用的方法中继续这个过程。
除了以上的三个用途外,帧数据还可能包含一些依赖于实现的数据,比如调试的信息。
十二、本地方法堆栈
本地方法区依赖于虚拟机的不同实现。虚拟机的实现者可以自己决定使用哪一种机制去执行本地方法。
任何本地方法接口(Native Method Interface)都使用某种形式的本地方法堆栈。
十三、执行引擎
一个java虚拟机实现的核心就是执行引擎。在Java虚拟机规范,执行引擎被描述为一系列的指令。对于每一个指令,规范都描述了他们应该做什么,但是没有说要如何去做。
1、指令集
在Java虚拟机中一个方法的字节码流就是一个指令的序列。每一个指令由一个字节的操作码(Opcode)和可能存在的操作数(Operands)。操作 码指示去做什么,操作数提供一些执行这个操作码可能需要的额外的信息。一个抽象的执行引擎每次执行一个指令。这个过程发生在每一个执行的线程中。
有时,执行引擎可能会遇到一个需要调用本地方法的指令,在这种情况下,执行引擎会去试图调用本地方法,但本地方法返回时,执行引擎会继续执行字节码流中的下一个指令。本地方法也可以看成对Java虚拟机中的指令集的一种扩充。
决定下一步执行那一条指令也是执行引擎工作的一部分。执行引擎有三种方法去取得下一条指令。多数指令会执行跟在他会面的指令;一些像goto, return的指令,会在他们执行的时候决定他们的下一条指令;当一个指令抛出异常时,执行引擎通过匹配catch语句来决定下一条应该执行的指令。
平台独立性、网络移动性、安全性左右了Java虚拟机指令集的设计。平台独立性是指令集设计的主要影响因素之一。基于堆栈的结构使得Java虚拟机可以在 更多的平台上实现。更小的操作码,紧凑的结构使得字节码可以更有效的利用网络带宽。一次性的字节码验证,使得字节码更安全,而不影响太多的性能。
2、执行技术
许多种执行技术可以用在Java虚拟机的实现中:解释执行,及时编译(just-in-time compiling),hot-spot compiling,native execution in silicon。
3、线程
Java虚拟机规范定义了一种为了在更多平台上实现的线程模型。Java线程模型的一个目标时可以利用本地线程。利用本地线程可以让Java程序中的线程能过在多处理器机器上真正的同时执行。
Java线程模型的一个代价就是线程优先级,一个Java线程可以在1-10的优先级上运行。1最低,10最高。如果设计者使用了本地线程,他们可能将这 10个优先级映射到本地优先级上。Java虚拟机规范只定义了,高一点优先级的线程可以却一些cpu时间,低优先级的线程在所有高优先级线程都堵塞时,也 可以获取一些cpu时间,但是这没有保证:低优先级的线程在高优先级线程没有堵塞时不可以获得一定的cpu时间。因此,如果需要在不同的线程间协作,你必 须使用的“同步(synchronizatoin)”。
同步意味着两个部分:对象锁(object locking)和线程等待、激活(thread wait and notify)。对象锁帮助线程可以不受其他线程的干扰。线程等待、激活可以让不同的线程进行协作。
在Java虚拟机的规范中,Java线程被描述为变量、主内存、工作内存。每一个Java虚拟机的实例都有一个主内存,他包含了所有程序的变量:对象、数组合类变量。每一个线程都有自己的工作内存,他保存了哪些他可能用到的变量的拷贝。规则:
1)、从主内存拷贝变量的值到工作内存中
2)、将工作内存中的值写会主内存中
如果一个变量没有被同步化,线程可能以任何顺序更新主内存中的变量。为了保证多线程程序的正确的执行,必须使用同步机制。
十四、本地方法接口(Native Method Interface)
Java虚拟机的实现并不是必须实现本地方法接口。一些实现可能根本不支持本地方法接口。Sun的本地方法接口是JNI(Java Native Interface)。
十五、现实中的机器(The Real Machine)
十六、数学方法:仿真(Eternal Math : A Simulation)
转载地址: http://java.chinaitlab.com/Jvm/534088.html
JAVA中堆和栈的概念
http://zhidao.baidu.com/question/40380205.html
http://java.chinaitlab.com/base/745172.html
http://topic.csdn.net/u/20080305/15/56381da2-34d7-4b6f-909e-43f7aee035e3.html?370807720
VirtualBox
http://www.virtualbox.org/wiki/Downloads
http://dlc.sun.com/virtualbox/vboxdownload.html#windows
http://dlc-cdn-rd.sun.com/c1/virtualbox/2.0.4/VirtualBox-2.0.4-38406-Win_x86.msi?e=1226673525&h=5f4c63618747d8f09a7f58997d0bc6f4
http://dlc-cdn-rd.sun.com/c1/virtualbox/2.0.4/virtualbox-2.0_2.0.4-38406_Ubuntu_intrepid_i386.deb?e=1226673531&h=2c60916180ea199c35a9b5d48e52549d
Sun xVM VirtualBox
-
Ubuntu 8.10 ("Intrepid Ibex") i386 | AMD64
-
Ubuntu 8.04 ("Hardy Heron") i386 | AMD64
http://www.virtualbox.org/wiki/Linux_Downloads
http://dlc-cdn-rd.sun.com/c1/virtualbox/2.0.4/virtualbox-2.0_2.0.4-38406_Ubuntu_hardy_i386.deb?e=1226743065&h=236f52a7ecae6175351db9e9e2faa842
Ctrl键+F键
屏幕分辨率改成800x600。
Debian-based Linux distributions: Add one of the following lines according to your distribution to your /etc/apt/sources.list :
deb http://download.virtualbox.org/virtualbox/debian intrepid non-free deb http://download.virtualbox.org/virtualbox/debian hardy non-free deb http://download.virtualbox.org/virtualbox/debian gutsy non-free deb http://download.virtualbox.org/virtualbox/debian dapper non-free deb http://download.virtualbox.org/virtualbox/debian lenny non-free deb http://download.virtualbox.org/virtualbox/debian etch non-free deb http://download.virtualbox.org/virtualbox/debian sarge non-free deb http://download.virtualbox.org/virtualbox/debian xandros4.0-xn non-free
The Sun public key for apt-secure can be downloaded here . You can add this key with
sudo apt-key add sun_vbox.asc
or combine downloading and registering:
wget -q http://download.virtualbox.org/virtualbox/debian/sun_vbox.asc -O- | sudo apt-key add -
The key fingerprint is
AF45 1228 01DA D613 29EF 9570 DCF9 F87B 6DFB CBAE Sun Microsystems, Inc. (xVM VirtualBox archive signing key)
开机就自动启动VMware并启动虚拟系统
http://os.rdxx.com/Linux/LinuxApply/2008/11/2117551619522.shtml
下载 VirtualBox 2.1.0
- VirtualBox 2.1.0 for Windows hosts x86 (Windows 32 位) | AMD64 ( Windows 64 位 )
- VirtualBox 2.1.0 for OS X hosts Intel Macs
- VirtualBox 2.1.0 for Linux hosts
- VirtualBox 2.1.0 for Solaris and OpenSolaris hosts x86 | AMD64
- VirtualBox 2.1.0 Software Developer Kit (SDK) All platforms
JVM 内存参数设置
1. Heap设定与垃圾回收
Java Heap分为3个区,Young,Old和Permanent。Young保存刚实例化的对象。当该区被填满时,GC会将对象移到Old区。Permanent区则负责保存反射对象,本文不讨论该区。
JVM的Heap分配可以使用-X参数设定,
-Xms
初始Heap大小
-Xmx
java heap最大值
-Xmn
young generation的heap大小
JVM有2个GC线程。第一个线程负责回收Heap的Young区。第二个线程在Heap不足时,遍历Heap,将Young 区升级为Older区。Older区的大小等于-Xmx减去-Xmn,不能将-Xms的值设的过大,因为第二个线程被迫运行会降低JVM的性能。
为什么一些程序频繁发生GC?有如下原因:
l 程序内调用了System.gc()或Runtime.gc()。
l 一些中间件软件调用自己的GC方法,此时需要设置参数禁止这些GC。
l Java的Heap太小,一般默认的Heap值都很小。
l 频繁实例化对象,Release对象。此时尽量保存并重用对象,例如使用StringBuffer()和String()。
如果你发现每次GC后,Heap的剩余空间会是总空间的50%,这表示你的Heap处于健康状态。许多Server端的Java程序每次GC后最好能有65%的剩余空间。
经验之谈:
1.Server端JVM最好将-Xms和-Xmx设为相同值。为了优化GC,最好让-Xmn值约等于-Xmx的1/3[2]。
2.一个GUI程序最好是每10到20秒间运行一次GC,每次在半秒之内完成[2]。
注意:
1.增加Heap的大小虽然会降低GC的频率,但也增加了每次GC的时间。并且GC运行时,所有的用户线程将暂停,也就是GC期间,Java应用程序不做任何工作。
2.Heap大小并不决定进程的内存使用量。进程的内存使用量要大于-Xmx定义的值,因为Java为其他任务分配内存,例如每个线程的Stack等。
2.Stack的设定
每个线程都有他自己的Stack。
-Xss
每个线程的Stack大小
Stack的大小限制着线程的数量。如果Stack过大就好导致内存溢漏。-Xss参数决定Stack大小,例如-Xss1024K。如果Stack太小,也会导致Stack溢漏。
3.硬件环境
硬件环境也影响GC的效率,例如机器的种类,内存,swap空间,和CPU的数量。
如果你的程序需要频繁创建很多transient对象,会导致JVM频繁GC。这种情况你可以增加机器的内存,来减少Swap空间的使用[2]。
4.4种GC
第一种为单线程GC,也是默认的GC。,该GC适用于单CPU机器。
第二种为Throughput GC,是多线程的GC,适用于多CPU,使用大量线程的程序。第二种GC与第一种GC相似,不同在于GC在收集Young区是多线程的,但在Old区和第一种一样,仍然采用单线程。-XX:+UseParallelGC参数启动该GC。
第三种为Concurrent Low Pause GC,类似于第一种,适用于多CPU,并要求缩短因GC造成程序停滞的时间。这种GC可以在Old区的回收同时,运行应用程序。-XX:+UseConcMarkSweepGC参数启动该GC。
第四种为Incremental Low Pause GC,适用于要求缩短因GC造成程序停滞的时间。这种GC可以在Young区回收的同时,回收一部分Old区对象。-Xincgc参数启动该GC。
4种GC的具体描述参见[3]。
参考文章:
1. JVM Tuning. http://java.sun.com/docs/hotspot/gc1.4.2/
文章出处:http://www.diybl.com/course/3_program/java/javajs/2008215/99778.html
历史篇:Java虚拟机家族考
http://icyfenix.iteye.com/blog/1133984
声明:本文为笔者原创,但首发于InfoQ中文站,详见文末声明。
说起Java虚拟机,许多Java程序员都会潜意识地把它与Sun[注1] HotSpot虚拟机等同看待,也许还有一些程序员会注意到BEA JRockit和IBM J9,但大多数人对JVM的认识都仅限于此了。
从1996年初Sun发布的JDK 1.0中所包含的Sun Classic VM算起,Java虚拟机已经发展了15个年头,沧海桑田一瞬间,15年转眼而过,这期间曾经涌现、湮灭过许多或经典或优秀或有特色的虚拟机实现,在 《Java虚拟机专栏》的第1篇中,我们先暂且把代码与技术放下,一起来回顾一下Java虚拟机家族的发展轨迹和历史变迁。
虚拟机始祖:Sun Classic / Exact VM
以今天的视角来看,Sun Classic VM的技术可能很原始,这款虚拟机的使命也早已终结。但仅凭它 “世界上第一款商用Java虚拟机”的头衔,就足够有令历史有记住它的理由。
1996年1月23日,Sun发布JDK 1.0,Java语言首次拥有了商用的正式运行环境,这个JDK中所带的虚拟机就是Classic VM。这款虚拟机只能使用纯解释器方式来执行Java代码,如果要使用JIT编译器那就必须进行外挂,但是假如外挂了JIT编译器,JIT编译器就完全接 管了虚拟机的执行系统,解释器便不再工作了。用户在这款虚拟机上执行java –version命令,将会看到类似下面这行的输出:
- java version "1.2.2"
- Classic VM (build JDK-1.2.2-001, green threads, sunwjit)
Sun的虚拟机团队努力去解决Classic VM所面临的各种问题,提升运行效率,在JDK 1.2时,曾在Solaris平台上发布过一款名为Exact VM的虚拟机,它的执行系统已经具备现代高性能虚拟机雏形:如两级即时编译器、编译器与解释器混合工作模式等。Exact VM因它使用准确式内存管理(Exact Memory Management,也可以叫Non-Conservative/Accurate Memory Management)而得名,即虚拟机可以知道内存中某个位置的数据具体是什么类型。譬如内存中有一个32bit的整数123456,它到底是一个 reference类型指向123456的内存地址还是一个数值为123456的整数,虚拟机将有能力分辨出来,这样才能在GC的时候准确判断堆上的数据 是否还可能被使用。由于使用了准确式内存管理,Exact VM可以抛弃掉以前Classic VM基于handler的对象查找方式(原因是GC后对象将可能会被移动位置,如果地址为123456的对象移动到654321,在没有明确信息表明内存 中哪些数据是reference的前提下,那虚拟机是不敢把内存中所有为123456的值改成654321的,所以要使用句柄来保持reference值 的稳定),这样每次定位对象都少了一次间接查找的开销,提升执行性能。
虽然Exact VM的技术相对Classic VM来说先进了许多,但是它命运显得十分英雄气短,在商业应用上只存在了很短暂的时间就被更为优秀的HotSpot VM所取代,甚至还没有来得及发布Windows和Linux平台下的商用版本。而Classic VM的生命周期则相对长了许多,它在JDK 1.2之前是Sun JDK中唯一的虚拟机,在JDK 1.2时,它与HotSpot VM并存,但默认是使用Classic VM(用户可用java –hotspot参数切换至HotSpot VM),而在JDK 1.3时,HotSpot VM成为默认虚拟机,它仍作为虚拟机的“备用选择”发布(使用java –classic参数切换),直到JDK 1.4的时候,Classic VM才正式退出商用虚拟机的历史舞台,与Exact VM一起进入了Sun Labs Research VM之中。
武林盟主:Sun HotSpot VM
HotSpot VM相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。但不一定所有人都知道的是,这个目前看起来“血统纯正”的虚拟机在最初并 非由Sun公司开发,而是由一家名为“Longview Technologies”的小公司设计的;甚至这个虚拟机最初并非是为Java语言而开发的,它来源于Strongtalk VM,而这款虚拟机中相当多的技术又是来源于一款支持Self语言实现“达到C语言50%以上的执行效率”的目标而设计的虚拟机,Sun公司注意到了这款 虚拟机在JIT编译上有许多优秀的理念和实际效果,在1997年收购了Longview Technologies公司,从而获得了HotSpot VM。
HotSpot VM既继承了Sun之前两款商用虚拟机的优点(如前面提到的准确式内存管理),也有许多自己新的技术优势,如它名称中的HotSpot指的就是它的热点代 码探测技术(这里的描写带有“历史由胜利者书写”的味道,其实两个VM基本上是同时期的独立产品,HotSpot还稍早一些,HotSpot一开始就是准 确式GC,而Exact VM之中也有与HotSpot几乎一样的热点探测,为了Exact VM和HotSpot VM哪个成为Sun主要支持的产品VM,在Sun公司内部还大吵过一场,HotSpot打败Exact并不能算技术上的胜利),HotSpot VM的热点代码探测能力可以通过执行计数器找出最具优编译价值的代码,然后通知JIT编译器以方法为单位进行编译。如果一个方法被频繁调用,或方法中有效 循环次数很多,将会分别触发标准编译和OSR(栈上替换)编译动作。通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳执行性能中取得 平衡,而且无需等待本地代码输出才能执行程序,即时编译的时间压力也相对减小,这样有助于引入更多的代码优化技术,输出质量更高的本地代码。
2006年的JavaOne大会上,Sun宣布最终会把Java开源,并在随后的一年,陆续地将JDK的各个部分(其中当然也包括了 HotSpot VM)在GPL协议下公开了源码,并在此基础上建立了OpenJDK。这样,HotSpot VM便成为了Sun JDK和OpenJDK两个实现极度接近的JDK项目的共同虚拟机。
在2008年和2010年,Oracle分别收购了BEA和Sun公司,这样Oracle就同时拥有了这个星球上最优秀的两款Java虚拟 机:JRockit VM和HotSpot VM。Oracle宣布在不久的将来(大约应在JDK 8的时候)会完成这两款虚拟机的整合工作,使之优势互补。整合的方式大致上是在HotSpot的基础上,移植JRockit的优秀特性,譬如使用 JRockit的垃圾回收器与MissionControl服务,使用HotSpot的JIT编译器与混合的运行时系统。当HotSpot吸收了 JRockit的全部功力之后,能否一统虚拟机的江湖,成为真正的武林盟主,我们拭目以待。
少数派:Sun Mobile-Embedded VM / Meta-Circular VM
Sun公司所研发的虚拟机可不仅有前面介绍到的服务器、桌面领域的商用虚拟机,除此之外,Sun面对移动和嵌入式市场,也发布过虚拟机产品, 另外还有一类虚拟机,在设计之初就没有抱着商用的目的,仅仅是用于研究、验证某种技术和观点,又或者是作为一些规范的标准实现。这些虚拟机对于大部分不从 事相关领域开发的Java程序员来说可能比较陌生,Sun公司发布的其他Java虚拟机[注2]有:
KVM
KVM中的K是“Kilobyte”的意思,它强调简单,轻量,高度可移植,但是运行速度比较慢。在Androd、iOS等智能手机操作系统出现前曾经在手机平台上得到非常广泛应用。
CDC/CLDC HotSpot Implementation
CDC/CLDC全称是Connected(Limited)Device Configuration,在JSR-139/JSR-218规范中进行定义,它希望在手机、电子书、PDA等设备上建立统一的Java编程接口,而 CDC-HI VM和CLDC-HI VM则是它们的一组参考实现。CDC/CLDC是整个Java ME的重要支柱,但从目前Android和Apple iOS二分天下的移动数字设备市场看来,在这个领域中,Sun的虚拟机所面临的局面远不如服务器和桌面领域乐观。
Squawk VM
Squawk VM是由Sun开发,运行于Sun SPOT(Sun Small Programmable Object Technology,一种手持的Wifi设备),也曾经运用于Java Card。这是一个Java代码比重很高的嵌入式虚拟机实现,其中诸如类加载器、字节码验证器、垃圾收集器、解释器、编译器和线程调度都是Java语言本 身所完成的,仅仅靠C语言来编写设备I/O和必要的本地代码。
JavaInJava
JavaInJava是Sun公司1997年~1998年间所研发的一个实验室性质的虚拟机,从名字就可以看出,它试图以Java语言来实现 Java语言本身的运行环境,既所谓的“元循环”(Meta-Circular,是指使用语言自身来实现其运行环境)。它必须运行在另外一个宿主虚拟机之 上,内部没有JIT编译器,代码只能以解释模式执行。在上世纪末主流Java虚拟机都未能很好解决性能问题的时代,开发这种项目,其执行速度大家可想而 知。
Maxine VM
Maxine VM和上面的JavaInJava非常相似,它也是一个几乎全部以Java代码实现(只有用于启动JVM的加载器使用C语言编写)的元循环Java虚拟 机。这个项目于2005年开始,到现在仍然在发展之中,比起JavaInJava,Maxine VM就显得“靠谱”很多,它有先进的JIT编译器和垃圾收集器(但没有解释器),可在宿主模式或独立模式下执行,其执行效率已经接近了HotSpot Client VM的水平。
百家争鸣:BEA JRockit / IBM J9 VM
前面介绍了Sun公司的各种虚拟机,除了Sun公司以外,其他组织、公司也研发过不少虚拟机实现,其中规模最大、最著名的就是BEA和IBM公司了。
JRockit VM曾经号称“世界上速度最快的Java虚拟机”(广告词,貌似J9 VM也这样说过),它是BEA公司在2002年从Appeal Virtual Machines公司收购获得的虚拟机。BEA将其发展为一款专门为服务器硬件和服务端应用场景高度优化的虚拟机,由于专注于服务端应用,它可以不太关注 于程序启动速度,因此JRockit内部不包含解析器实现,全部代码都靠即时编译器编译后执行。除此之外,JRockit的垃圾收集器和 MissionControl服务套件等部分的实现,在众多Java虚拟机中也一直处于领先水平。
IBM J9 VM并不是IBM公司唯一的Java虚拟机,不过是目前IBM主力发展的Java虚拟机,J9原本是内部开发代号,正式名称是“IBM Technology for Java Virtual Machine”,简称IT4J,只是这个名字太拗口了一点,普及程度不如J9。J9 VM最初是由IBM Ottawa实验室一个SmallTalk的虚拟机扩展而来的,当时这个虚拟机有一个bug是因为8k值定义错误引起,工程师们花了很长时间终于发现并解 决了这个错误,此后这个版本的虚拟机就被称为K8了,后来扩展出支持Java的虚拟机就被称为J9了。与BEA JRockit专注于服务端应用不同,IBM J9的市场定位与Sun HotSpot比较接近,它是一款设计上从服务端到桌面应用再到嵌入式都全面考虑的多用途虚拟机,J9的开发目的是作为IBM公司各种Java产品的执行 平台,它的主要市场在和IBM产品(如IBM WebSphere等)搭配以及在IBM AIX和z/OS这些平台上部署Java应用。
除了BEA和IBM外,其他一些大公司如HP、SAP等也号称有自己的专属JDK和虚拟机,但是它们是通过从Sun购买版权的方式获得的,并非自己独立开发。
最终兵器:Azul VM / BEA Liquid VM
我们平时所提及的“高性能Java虚拟机”一般是指HotSpot、JRockit、J9这类在通用平台上运行的商用虚拟机,但其实Azul VM和BEA Liquid VM这类特定硬件平台专有的虚拟机才是“高性能”的最终兵器。
Azul VM是Azul Systems 公司在HotSpot基础上进行大量改进,运行于Azul Systems 公司的专有硬件Vega系统上的Java虚拟机,每个Azul VM实例都可以管理至少数十个CPU和数百GB的内存的硬件资源,并提供在巨大内存范围内实现可控的GC时间的垃圾收集器、为专有硬件优化的线程调度等优 秀特性。在2010年,Azul开始从硬件转向软件,发布了自己的Zing JVM,可以在通用x86平台上提供接近于Vega系统的特性。
Liquid VM即是现在的JRockit VE(Virtual Edition),它是BEA公司开发的,可以直接运行在自家Hypervisor系统上的JRockit VM的虚拟化版本,Liquid VM不需要操作系统的支持,或者说它自己本身实现了一个专用操作系统的必要功能,如文件系统、网络支持等。由虚拟机越过通用操作系统直接控制硬件可以获得 很多好处,如在线程调度时,不需要再进行内核态/用户态的切换等,这样可以最大限度地发挥硬件的能力,提升Java程序的执行性能。
挑战者:Apache Harmony / Google Android Dalvik VM
这节介绍的Harmony VM和Dalvik VM只能称作“虚拟机”,而不能称作“Java虚拟机”,但是这两款虚拟机(以及所代表的技术体系)对最近三年的Java世界产生了非常大的影响和挑战,甚至有悲观的评论家认为成熟的Java生态系统有崩溃的可能。
Apache Harmony是一个Apache软件基金会旗下以Apache License协议开源的实际兼容于JDK 1.5和JDK 1.6的Java程序运行平台,这个介绍相当拗口。它包含自己的虚拟机和Java库,用户可以在上面运行Eclipse、Tomcat、Maven等常见 的Java程序,但是……它没有通过TCK认证,所以我们不得不用那么一长串拗口的语言来介绍它,而不能用一句“Apache的JDK”来说明。如果一个 公司要宣布自己的运行平台“兼容于Java语言”,那就必须要通过TCK(Technology Compatibility Kit)的兼容性测试,Apache基金会曾要求Sun公司提供TCK的使用授权,但是一直遭到拒绝,直到Oracle收购了Sun公司之后,双方关系越 闹越僵,最终导致Apache愤然退出JCP(Java Community Process)组织,这是近代Java社区最严重的一次分裂。
在Sun把JDK开源形成OpenJDK之后,Apache Harmony开源的优势被极大地削弱,甚至连Harmony项目的最大参与者IBM公司也宣布辞去Harmony项目管理主-席的职位,参与 OpenJDK项目的开发。虽然Harmony没有真正大规模商业运用过,但是它的许多代码(基本上是Java库部分的代码)被吸纳进IBM的JDK7实 现以及Google Android SDK之中,尤其是对Android的发展起了很大推动作用。
说到Android,这个时下最热门的移动数码设备平台在最近3年间的发展所取得的成果已经远远超越了Java ME在过去十多年所获得的成果,Android让Java语言真正走进了移动数码设备领域,只是走的并非Sun公司原本想象的那一条路。
Dalvik VM是Android平台的核心组成部分之一,它名字来源于冰岛一个名为Dalvik的小渔村。Dalvik VM并不是一个Java虚拟机,它没有遵循Java虚拟机规范,不能直接执行Java的class文件,使用寄存器架构而不是JVM中常见的栈架构。但是 它与Java却又有着千丝万缕的联系,它执行dex(Dalvik Executable)文件可以通过class文件转化而来,使用Java语法编写应用程序,可以直接使用大部分的Java API等等。目前Dalvik VM随着Android一起处于迅猛发展阶段,在Android 2.2中已提供即时编译器实现,执行性能有了很大的提高。
没有成功,但并非失败:Mircosoft JVM及其他
在十几年的Java虚拟机发展历程中,除去上面介绍那些被大规模商业应用过的Java虚拟机外,还有许多虚拟机是不为人知或者曾经绚丽过但最终湮灭的。我们以其中Mircorsoft公司的JVM来介绍一下。
也许Java程序员听起来可能会觉得惊讶,微软曾经是Java技术的铁杆支持者。在Java语言诞生的初期(1996年~1998年,以 JDK1.2发布之前为分界),它的主要的应用之一是在浏览器中运行Java Applets程序,微软为了在IE3中支持Java Applets应用而开发了自己的Java虚拟机,虽然这款虚拟机只有Windows平台的版本(这很正常吧?),但却是当时Windows下性能最好的 Java虚拟机,它在1997和1998连续两年获得了《PC Magazine》杂志的“编辑选择奖”。但好景不长,在1997年10月,Sun公司正式以侵犯商标、不正当竞争等罪名控告微软,在随后对微软公司的垄 断调查之中,这款虚拟机也曾作为证据之一被呈送法庭。这场官司的结果是微软赔偿2000万美金给Sun,承诺终止其Java虚拟机的发展,并逐步在产品中 移除Java虚拟机相关功能。
我们试想一下,如果当年Sun没有起诉微软公司,微软继续保持着对Java技术的热情,那Java的世界会变得更好还是更坏?.NET技术是否会发展起来?但历史是没有如果的。其他在本文中没有介绍到的Java虚拟机还包括有(当然,应该还有很多笔者所不知道的):
- JamVM:http://jamvm.sourceforge.net/
- cacaovm:http://www.cacaovm.org/
- SableVM:http://www.sablevm.org/
- Kaffe:http://www.kaffe.org/
- Jelatine JVM:http://jelatine.sourceforge.net/
- NanoVM:http://www.harbaum.org/till/nanovm/index.shtml
- MRP:http://mrp.codehaus.org/
- Moxie JVM:http://moxie.sourceforge.net/
- Jikes RVM:http://jikesrvm.org/
本文撰写时主要参考了以下资料:
- http://en.wikipedia.org/wiki/Java_virtual_machine
- http://wikis.sun.com/display/HotSpotInternals/Home
- http://rednaxelafx.iteye.com/blog/858009
本文已经首发于 InfoQ中文站,版权所有,原文为 《Java虚拟机家族考》,如需转载,请务必附带本声明,谢谢。
InfoQ中文站是一个面向中高端技术人员的在线独立社区,为Java、.NET、Ruby、SOA、敏捷、架构等领域提供及时而有深度的资讯、高端技术大会如QCon 、线下技术交流活动QClub、免费迷你书下载如《架构师》等。
脚注:
注1:Sun与BEA分别在2010、2008年被Oracle公司收购,由于本文涉及到大量历史事件,为了避免混乱,依然保留Sun和BEA的名称。
注2:这里把JavaME里面的虚拟机列为“少数派”是从大多数Java程序员的了解程度出发,从虚拟机部署数量来讲,JavaME远比JavaEE虚拟机多,毕竟服务器应用无法在数量上和移动、嵌入式设备比较的。
end