1、JVM 详解本文详细讲解了 JVM(Java Virtual Machine)的方方面面,首先由 java 的特性来描绘JVM 的大致应用, 再细细阐述了 JVM 的原理及内存管理机制和调优.最后讲述了与 JVM密切相关的 Java GC 机制.本文内容大多来自网络,但内容十分丰富, 是学习 JVM 的好资料.后面会再针对 JVM 的两大职责 class loader 和 execution engine 进行讲解若有疑问目录Java 相关 .11.1Java 定义 11.2Java 的开发流程 11.3Java 运行的原理 21.4 半编译半解释 31.5 平台无关性 4JVM 内存模型 4
2、2.1 JVM 规范 .52.2 Sun JVM .82.3 SUN JVM 内存管理( 优化 ).102.4 SUN JVM 调优 .132.5.JVM 简单理解 .162.5.1 Java 栈 162.5.2 堆 .162.5.3 堆栈分离的好处 .202.5.4 堆 (heap)和栈(stack) .20JAVA 垃圾收集器 .213.1 垃圾收集简史 213.2 常见的垃圾收集策略 213.2.1 Reference Counting(引用计数) 223.2.2 跟踪收集器 .223.3 JVM 的垃圾收集策略 .273.3.1 Serial Collector 283.3.2 Par
3、allel Collector293.3.3 Concurrent Collector 30Java 虚拟机(JVM)参数配置说明 30Java 相关1.1Java 定义1.2Java 的开发流程1.3Java 运行的原理1.4 半编译半解释1.5 平台无关性JVM 内存模型 2.1 JVM 规范JVM specification 对 JVM 内存的描述 首先我们来了解 JVM specification 中的 JVM 整体架构。如下图:主要包括两个子系统和两个组件: Class loader(类装载器) 子系统,Execution engine(执行引擎) 子系统;Runtime data
4、area (运行时数据区域)组件, Native interface(本地接口)组件。 Class loader 子系统的作用 :根据给定的全限定名类名(如 java.lang.Object)来装载 class 文件的内容到 Runtime data area 中的method area(方法区域)。Javsa 程序员可以 extends java.lang.ClassLoader类来写自己的 Class loader。 Execution engine 子系统的作用 :执行 classes 中的指令。任何 JVM specification 实现(JDK)的核心是 Execution eng
5、ine, 换句话说:Sun 的JDK 和 IBM 的 JDK 好坏主要取决于他们各自实现的 Execution engine 的好坏。每个运行中的线程都有一个 Execution engine 的实例。 Native interface 组件 :与 native libraries 交互,是其它编程语言交互的接口。 Runtime data area 组件:这个组件就是 JVM 中的内存。 下面对这个部分进行详细介绍。 Runtime data area 的整体架构图Runtime data area 主要包括五个部分:Heap (堆), Method Area(方法区域), Java Sta
6、ck(java 的栈), Program Counter(程序计数器), Native method stack(本地方法栈)。Heap 和 Method Area 是被所有线程的共享使用的;而 Java stack, Program counter 和 Native method stack 是以线程为粒度的,每个线程独自拥有。 Heap Java 程序在运行时创建的所有类实或数组都放在同一个堆中。而一个 Java 虚拟实例中只存在一个堆空间,因此所有线程都将共享这个堆。每一个 java 程序独占一个 JVM 实例,因而每个 java 程序都有它自己的堆空间,它们不会彼此干扰。但是同一 ja
7、va 程序的多个线程都共享着同一个堆空间,就得考虑多线程访问对象(堆数据)的同步问题。 (这里可能出现的异常java.lang.OutOfMemoryError: Java heap space) Method area 在 Java 虚拟机中,被装载的 class 的信息存储在 Method area 的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的 class 文件,然后读入这个class 文件内容并把它传输到虚拟机中。紧接着虚拟机提取其中的类型信息,并将这些信息存储到方法区。该类型中的类(静态)变量同样也存储在方法区中。与 Heap 一样,method area 是多线程共享的
8、,因此要考虑多线程访问的同步问题。比如,假设同时两个线程都企图访问一个名为 Lava 的类,而这个类还没有内装载入虚拟机,那么,这时应该只有一个线程去装载它,而另一个线程则只能等待。 (这里可能出现的异常 java.lang.OutOfMemoryError: PermGen full)Java stack Java stack 以帧为单位保存线程的运行状态。虚拟机只会直接对 Java stack 执行两种操作:以帧为单位的压栈或出栈。每当线程调用一个方法的时候,就对当前状态作为一个帧保存到 java stack 中(压栈);当一个方法调用返回时,从 java stack 弹出一个帧(出栈)。
9、栈的大小是有一定的限制,这个可能出现 StackOverFlow 问题。 下面的程序可以说明这个问题。public class TestStackOverFlow public static void main(String args) Recursive r = new Recursive();r.doit(10000);/ Exception in thread “main“ java.lang.StackOverflowErrorclass Recursive public int doit(int t) if (t softreference = new SoftReference(s
10、tr);str=null;System.gc();assertNotNull(softreference.get();Weak reference弱引用有利于对象更快的被回收,假如一个对象没有强引用只有弱引用,那么在 GC 后,这个对象肯定会被回收。Public void testWeakReference()String str = “test“;WeakReference weakReference = new WeakReference(str);str=null;System.gc();assertNull(weakReference.get();Phantom reference 3
11、.2.2.1 Mark-Sweep Collector(标记 -清除收集器) 标记清除收集器最早由 Lisp 的发明人于 1960 年提出,标记清除收集器停止所有的工作,从根扫描每个活跃的对象,然后标记扫描过的对象,标记完成以后,清除那些没有被标记的对象。优点:1 解决循环引用的问题2 不需要编译器的配合,从而就不执行额外的指令缺点:1每个活跃的对象都要进行扫描,收集暂停的时间比较长。3.2.2.2 Copying Collector(复制收集器) 复制收集器将内存分为两块一样大小空间,某一个时刻,只有一个空间处于活跃的状态,当活跃的空间满的时候,GC 就会将活跃的对象复制到未使用的空间中去,
12、原来不活跃的空间就变为了活跃的空间。复制收集器具体过程可以参考下图:优点:1 只扫描可以到达的对象,不需要扫描所有的对象,从而减少了应用暂停的时间缺点:1需要额外的空间消耗,某一个时刻,总是有一块内存处于未使用状态2复制对象需要一定的开销3.2.2.3 Mark-Compact Collector(标记 -整理收集器) 标记整理收集器汲取了标记清除和复制收集器的优点,它分两个阶段执行,在第一个阶段,首先扫描所有活跃的对象,并标记所有活跃的对象,第二个阶段首先清除未标记的对象,然后将活跃的的对象复制到堆得底部。标记整理收集器的过程示意图请参考下图:Mark-compact 策略极大的减少了内存碎
13、片,并且不需要像 Copy Collector一样需要两倍的空间。3.3 JVM 的垃圾收集策略 GC 的执行时要耗费一定的 CPU 资源和时间的,因此在 JDK1.2 以后,JVM 引入了分代收集的策略,其中对新生代采用“Mark-Compact“策略,而对老生代采用了“Mark-Sweep“的策略。其中新生代的垃圾收集器命名为“minor gc”,老生代的 GC 命名为“Full Gc 或者 Major GC“.其中用 System.gc()强制执行的是 Full Gc.3.3.1 Serial Collector Serial Collector 是指任何时刻都只有一个线程进行垃圾收集,
14、这种策略有一个名字“stop the whole world“, 它需要停止整个应用的执行。这种类型的收集器适合于单 CPU 的机器。Serial Copying Collector此种 GC 用-XX:UseSerialGC 选项配置,它只用于新生代对象的收集。1.5.0 以后.-XX:MaxTenuringThreshold 来设置对象复制的次数。当 eden 空间不够的时候,GC 会将 eden 的活跃对象和一个名叫 From survivor 空间中尚不够资格放入 Old 代的对象复制到另外一个名字叫 To Survivor 的空间。而此参数就是用来说明到底 From survivor
15、 中的哪些对象不够资格,假如这个参数设置为 31,那么也就是说只有对象复制 31 次以后才算是有资格的对象。这里需要注意几个个问题: From Survivor 和 To survivor 的角色是不断的变化的,同一时间只有一块空间处于使用状态,这个空间就叫做 From Survivor 区,当复制一次后角色就发生了变化。 如果复制的过程中发现 To survivor 空间已经满了,那么就直接复制到 old generation. 比较大的对象也会直接复制到 Old generation,在开发中,我们应该尽量避免这种情况的发生。Serial Mark-Compact Collector串行的标记-整理收集器是 JDK5 update6 之前默认的老生代的垃圾收集器,此收集使得内存碎片最少化,但是它需要暂停的时间比较长3.3.2 Parallel Collector Parallel Collector 主要是为了应对多 CPU,大数据量的环境。Parallel Collector 又可以分为以下两种:Parallel Copying Collector