JVM概论

引子

  Java虚拟机是Java应用程序的执行环境。通常而言,JVM是由一组严格的指令集和一个复杂的内存模型来具体实现的虚拟机,它用来解释编译好的java字节码文件,将字节码转换为特定机器可以执行的本机代码(native code)。它也可以指代某一软件运行时的进程实例。这里,我们以hotspot实现的JVM为例。

  JVM的规则保证任何一款具体实现的JVM都要以完全相同的方式去解释java字节码文件,无论是一个进程,一个独立的java操作系统,抑或是一个直接执行字节码命令的处理器芯片。一般情况下,我们通常讨论的JVM是一个运行在操作系统上的进程。

  JVM的架构设计使得它可以精细的控制JAVA应用程序的每一个动作,在没有权限的情况下,应用程序无法去访问本地文件系统,处理器,网络等。例如,在远程操作的情况下,代码需要有签名证书。

  除了去解释java字节码,许多软件实现的JVM都有一个JIT编译器用于生成频繁执行的方法机器代码。机器代码是可以直接被cpu解析执行的,所以比字节码速度更快。

你无需去理解JVM的内部,就能编写并运行一个JAVA应用程序。但是,如果你知道了其中的一些原理,就能避免一些性能上的问题。本文以sunspot为例子来说明。

架构

  JVM主要有两大子系统:

  • 类加载器:负责读取java源文件,并把类加载进内存。
  • 执行引擎:负责执行指令。

  这里的内存是底层操作系统分配给JVM的,如下所示:

类加载器

  JVM应用不同类型的类加载器构造了层次结构:

  • bootstrap类加载器,它是其他类加载器的父母,负责加载核心java类库,并且是唯一的一个使用本机代码进行编写的类加载器。
  • 扩展类加载器,它是bootstrap类加载器的孩子,负责加载扩展类库。
  • 系统类加载器,它是扩展类加载器的的孩子, 负责从classpath中加载类文件。
  • 用户自定义类加载器,它是系统类加载器或其他用户自定义类加载器的孩子。

  当类加载器收到去加载一个类的请求时,会去检查cache中该类是否已经被加载,然后向其父加载器发出加载请求,如果其父加载器加载失败,那么它就自己进行加载。一个子类加载器可以检查其父类加载器的cache中是否加载了某个类,但是父类加载器无法查看子类cache中的缓存。这样设计的原因是为了防止子类加载器加载那些已经被父类加载器加载过的类。(呼,好绕口。。。)

执行引擎

  执行引擎负责执行被加载进内存的字节码指令,为了使计算机能够识别字节码,执行引擎采用了两种方式:

  • 解释:执行引擎将字节码解释成机器语言。
  • JIT编译:如果一个方法被频繁的执行,执行引擎会把该方法的字节码编译成本机代码存于cache中,于是,所有与该方法相关的指令都无需解释,直接执行。

  尽管JIT的编译过程比普通的解释过程要耗时,但是它只需编译一次,对于那些上千次调用的方法来说,直接执行机器代码就比每次都要转换字节码再执行要划算了。

  JIT编译器对于JVM而言并非是必须的组件,同时,也不是提升JVM性能的唯一手段。JVM规范只是定义了字节码与机器代码的对应关系,至于如何具体实现,就是不同版本JVM的事情了。

内存模型

  JAVA内存模型是建立在内存自动管理机制之上的。当一个对象不在被应用程序引用,垃圾收集器GC就丢弃它并释放内存。这与其他编程语言需要手动释放对象的方式不同。

  JVM从操作系统中申请来内存,并分割成如下几个区域:

  • 堆:存放对象实例的共享区,GC会来进行扫描。
  • 方法区:用于存放类加载器加载进来的字节码,静态变量,常量等等。最近,它被JVM移除,类被当做元数据加载进本机操作系统的内存。
  • 栈:存放基本类型变量和类对象实例的引用,线程私有。

  将堆根据类实例的生存周期划分为不同区域使得内存管理更加有效,GC无需遍历整个堆。绝大多数对象的生命周期都很短,而那些略长一些的对象所占内存在程序结束之前不大可能被全部回收。

  当一个类对象被创建,它会被存于堆的eden池中,当Eden池满时就会触发young GC。首先,GC标记那些已经废弃的对象,然后增加那些仍然存活的对象的"寿命值"(使用经历过垃圾回收次数来表示)。然后进行Eden区+有对象的Survivor区(设为S0区)垃圾回收,把存活的对象用复制算法拷贝到一个空的Survivor(S1)中,此时Eden区被清空,另外一个Survivor S0也为空。下次触发Young GC回收Eden+S1,将存活对象拷贝到S0中。新生代垃圾回收简单、粗暴、高效。足够Old,也拷贝到Old区(每次Young GC都会使Survivor区存活对象值+1,直到阈值)。

  若发现Survivor区满了,则将这些对象拷贝到old区或者Survivor没满但某些对象的"寿命值"达到阈值,它们将进入老年期并被放入tenured池中,tenured池也会进行垃圾收集,发生一次 Major GC 至少伴随一次Young GC,一般比JVM在tenured区申请不到内存,会进行Full GC。tenured区使用一般采用Concurrent-Mark–Sweep策略回收内存。

当一个GC运行时,应用程序所有的进程都将停止。Young GC很频繁,但是会很快清理Eden池中的对象。而Major GC由于涉及到大量仍存活的对象,所以比Young GC慢很多。

堆内存是动态的。当堆内存满时,JVM会重新分配内存给它直到最大限度,同时也停止应用程序进程来完成内存分配。

线程

  JVM是一个单进程,但是它可以并发多个线程,不同线程执行自己的方法。所有的线程共享着JVM分配到的资源。JVM进程在程序入口(main方法)新开一个线程,其余的线程都来自与此线程,并独立执行。多个线程可以并发地在不同处理器中执行,或者共享同一个处理器。

时间: 2024-08-27 09:46:30

JVM概论的相关文章

JVM调优总结 -Xms -Xmx -Xmn -Xss

http://unixboy.iteye.com/blog/174173/ 堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理内存限制.32位系统下,一般限制在1.5G~2G:64为操作系统对内存无限制.我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m.典型设置: java -Xmx3550m -Xms3550m -Xmn2g -Xss128k-

JVM垃圾回收机制总结(5) :JDK垃圾收集器的配置命令

以下配置主要针对分代垃圾回收算法而言. 堆大小设置 年轻代的设置很关键 JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理内存限制.32位系统下,一般限制在1.5G~2G:64为操作系统对内存无限制.在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m. 典型设置: java -Xmx3550m -Xms3550m -Xmn2g –Xss128k -Xmx3550

JVM调优经验分享

前言 一.JVM调优知识背景简介 二.JVM调优参数简介 三.JVM调优目标 四.JVM调优经验 结束语 <br/> 本次分享探讨的JVM调优是指server端运行的JVM调优,适应版本为[1.6– 1.7], 不涉及最新的1.8版本. 假设线程池.连接池.程序代码等都已经做过优化,效果(系统吞吐量.响应性能)仍然不理想,我们就可以考虑JVM调优了. <br/> 一. JVM调优知识背景简介 1.堆与栈的概念 堆和栈是程序运行的关键:栈是运行时的单位,而堆是存储的单位. 栈解决程序

JVM学习笔记

看的<深入理解Java虚拟机> ---------------Java内存区域------------- 一.运行时数据区域 1.Java虚拟机管理的内存包括的运行时数据区:程序计数器Program Couter Register,虚拟机栈VM Stack,本地方法栈Native Method Stack,堆Heap,方法区Method Area.2.程序计数器:当前线程所执行的字节码的行号指示器.字节码解释器工作时通过改变计数器的值来选取下一条需要执行的字节码指令,分之.循环.跳转.异常处理

JVM调优总结

一.相关概念 基本回收算法 引用计数(Reference Counting) 比较古老的回收算法.原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数.垃圾回收时,只用收集计数为0的对象.此算法最致命的是无法处理循环引用的问题. 标记-清除(Mark-Sweep) 此算法执行分两阶段.第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除.此算法需要暂停整个应用,同时,会产生内存碎片. 复制(Copying) 此 算法把内存空间划为两个相等的区域,每次

JVM内存配置详解(转)

前段时间在一个项目的性能测试中又发生了一次OOM(Out of swap sapce),情形和以前网店版的那次差不多,比上次更奇怪的是,此次搞了几天之后啥都没调整系统就自动好了,死活没法再重现之前的OOM了!问题虽然蹊跷,但也趁此机会再次对JVM堆模型.GC垃圾算法等进行了一次系统梳理: 基本概念 堆/Heap JVM管理的内存叫堆:在32Bit操作系统上有4G的限制,一般来说Windows下为2G,而Linux 下为3G:64Bit的就没有这个限制. JVM初始分配的内存由-Xms指定,默认是

JVM调优及参数设置

(1)参数 -Xms:初始堆大小 -Xmx :最大堆大小 此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存 -Xmn :年轻代大小 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小.持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8. -XX:NewSize:设置年轻代大小 -XX:MaxNewSize:年轻代最大值 -XX:NewRatio 年老代与年轻代的比值 -XX:SurvivorRat

JVM 调优参数详解

GC有两种类型:Scavenge GC 和Full GC 1.Scavenge GC 一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,堆的Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor的两个区中. 2.Full GC 对整个堆进行整理,包括Young.Tenured和Perm.Full GC 比Scavenge GC要慢,因此应该尽可能减少Full GC,有如下原因可能导致Full GC a.Tenured被写满: b.P

jvm相关参数,调优

常见的jvm参数如下 -Xmx1024m:设置JVM最大可用内存为1024M. -Xms1024m:设置JVM促使内存为1024m.此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存. -Xmn512m:设置年轻代大小为512m.整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小.持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8. -Xss128k:设置每个线程的堆栈大小.JDK5.0以后