jvm是什么
要理解 java 虚拟机,我们得先知道java的运行原理
先来看张图:
看了图就知道 java 虚拟机是用来解释我们编译过的java 代码的
可以把jvm当成一个功能完善的虚拟计算机,如有处理器,堆栈,寄存器等...
再来看张 jvm介绍中给出的一张架构图:
Paste_Image.png图中讲jvm 分成了两个子系统和两个组件:Class loader(类装载器) 子系统,Execution engine(执行引擎) 子系统;Runtime data area (运行时数据区域)组件, Native interface(本地接口:如hashcode()就是本地方法)组件
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 engine, 换句话说:Sun 的JDK 和 IBM的 JDK好坏主要取决于他们各自实现的 Execution engine的好坏。每个运行中的线程都有一个 Execution engine的实例。
Native interface组件 :与 native libraries交互,是其它编程语言交互的接口。
Runtime data area 组件:这个组件就是 JVM中的内存**。
对于Euntime data area 我们应该会碰到内存溢出 OutOfMemoryError 问题,这里就跟这个组件有关了,那就先来介绍一个它:
大致架构图
Runtime data area 主要包括五个部分:Heap (堆),Method Area(方法区域),Java Stack(java的栈),Program Counter(程序计数器),Native method,stack(本地方法栈)。
Heap 和 Method Area是被所有线程的共享使用的;java stack, Program counter 和 Native method stack是以线程为粒度的。
Heap(堆) 一个jvm实例只有一个Heap,所以线程之间是公用的,这里可能会抛 java.lang.OutOfMemoryError: Java heap space 异常,这里对应着堆空间,为了便于垃圾回收,又将其细分为 新生区 { 伊甸园(eden),幸存者0区,幸存者1区 },年老区(Tenured),垃圾回收的机制是:当有新的对象在新生区生成时,先判断是否满了,如满了就执行垃圾回收,将无用的对象回收,并将还被引用的对象移往下一个区域,以此类推,直到这5个区域都满了,就会报JVM 堆空间内存溢出java.lang.OutOfMemoryError: Java heap space 异常
**Method Area(方法区域) **对应永久存储区,用来装载类的信息,存放静态文件。这里有可能会抛java.lang.OutOfMemoryError: PermGen full,一般这里比较少报这个异常
Java 栈 Java 栈是与每一个线程关联的,JVM 在创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文。栈空间随着线程的终止而释放。
StackOverflowError:如果在线程执行的过程中,栈空间不够用,那JVM就会抛出此异常,这种情况一般是死递归造成的。
堆栈的区别 总体来说: 栈--主要存放引用 和基本数据类型。
堆--用来存放 new 出来的对象实例。
jvm优化
现在估计大家大致对jvm有了一个大概的了解了,那么问题来了。说了这么多,还不是为了jvm调优吗,那么怎么优化jvm呢?
这里我总结几个常用配置:
-server 启用能够执行优化的编译器, 显著提高服务器的性能,但使用能够执行优化的编译器时,服务器的预备时间将会较长。生产环境的服务器强烈推荐设置此参数
-Xss 单个线程堆栈大小值;JDK5.0以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为 256K。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-Xms 启动应用时,JVM堆空间的初始大小值。
-Xmx 应用运行中,JVM堆空间的极限值。为了不消耗扩大 JVM堆控件分配的开销,将此参数和-Xms这个两个值设为相等,考虑到需要开线程,讲此值设置为总内存的 80%.
-Xmn 此参数硬性规定堆空间的新生代空间大小,推荐设为堆空间大小的 1/4
-XX:PermSize 应用服务器启动时,永久存储区的初始内存大
-XX:MaxPermSize 应用运行中,永久存储区的极限值。为了不消耗扩大 JVM永久存储区分配的开销,将此参数和-XX:PermSize这个两个值设为相等。
垃圾回收相关:
http://www.cnblogs.com/dongguol/p/6111256.html
典型的垃圾收集算法
在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是开始进行垃圾回收,但是这里面涉及到一个问题是:如何高效地进行垃圾回收。由于Java虚拟机规范并没有对如何实现垃圾收集器做出明确的规定,因此各个厂商的虚拟机可以采用不同的方式来实现垃圾收集器,所以在此只讨论几种常见的垃圾收集算法的核心思想。
1.Mark-Sweep(标记-清除)算法
这是最基础的垃圾回收算法,之所以说它是最基础的是因为它最容易实现,思想也是最简单的。标记-清除算法分为两个阶段:标记阶段和清除阶段。标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。具体过程如下图所示:
image从图中可以很容易看出标记-清除算法实现起来比较容易,但是有一个比较严重的问题就是容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
2.Copying(复制)算法
为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。具体过程如下图所示:
image这种算法虽然实现简单,运行高效且不容易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能够使用的内存缩减到原来的一半。
很显然,Copying算法的效率跟存活对象的数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将会大大降低。
3.Mark-Compact(标记-整理)算法
为了解决Copying算法的缺陷,充分利用内存空间,提出了Mark-Compact算法。该算法标记阶段和Mark-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:
image4.Generational Collection(分代收集)算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
目前大部分垃圾收集器对于新生代都采取Copying算法,因为新生代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分新生代的空间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
而由于老年代的特点是每次回收都只回收少量对象,一般使用的是Mark-Compact算法。
注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。
Minor GC 和 Full GC
新生代 GC(Minor GC):
指发生在新生代的垃圾收集动作,因为 Java 对象大多都具 备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。
老年代 GC(Major GC / Full GC):指发生在老年代的 GC,出现了 Major GC,经常 会伴随至少一次的 Minor GC(但非绝对的,在 ParallelScavenge 收集器的收集策略里 就有直接进行 Major GC 的策略选择过程) 。MajorGC 的速度一般会比 Minor GC 慢 10 倍以上。
虚拟机给每个对象定义了一个对象年龄(Age)计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC,年龄就增加 1 岁,当它的年龄增加到一定程度(默认为 15 岁)时,就会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置
-XX:+UseParNewGC 可用来设置年轻代为并发收集【多 CPU】,如果你的服务器有多个 CPU,你可以开启此参数;开启此参数,多个 CPU可并发进行垃圾回收,可提高垃圾回收的速度。此参数和+UseParallelGC,-XX:ParallelGCThreads搭配使用。
+UseParallelGC 选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集 。可提高系统的吞吐量。
-XX:ParallelGCThreads 年轻代并行垃圾收集的前提下(对并发也有效果)的线程数,增加并行度,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
GC日志
-XX:+PrintGC 输出GC日志
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径
网友评论