`
chriszeng87
  • 浏览: 738405 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JVM必备指南

    博客分类:
  • Java
阅读更多

本文由 ImportNew - xiafei 翻译自 anturis

转自:http://www.importnew.com/13556.html

 

简介

Java虚拟机(JVM)是Java应用的运行环境,从一般意义上来讲,JVM是通过规范来定义的一个虚拟的计算机,被设计用来解释执行从Java源码编译而来的字节码。更通俗地说,JVM是指对这个规范的具体实现。这种实现基于严格的指令集和全面的内存模型。另外,JVM也通常被形容为对软件运行时环境的实现。通常JVM实现主要指的是HotSpot。

JVM规范保证任何的实现都能够以同样的方式解释执行字节码。其实现可以多样化,包括进程、独立的Java操作系统或者直接执行字节码的处理器芯片。我们了解最多的JVM是作为软件实现,运行在流行的操作系统平台上(包括Windows、OS X、Linux和Solaris等)。

JVM的结构允许对一个Java应用进行更细微的控制。这些应用运行在沙箱(Sandbox)环境中。确保在没有恰当的许可时,无法访问到本地文件系统、处理器和网络连接。远程执行时,代码还需要进行证书认证。

除了解释执行Java字节码,大多数的JVM实现还包含一个JIT(just-in-time 即时)编译器,用于为常用的方法生成机器码。机器码使用的是CPU的本地语言,相比字节码有着更快的运行速度。

虽然理解JVM不是开发或运行Java程序的必要条件,但是如果多了解一些JVM知识,那么就有机会避免很多性能上的问题。理解了JVM,实际上这些问题会变得简单明了。

体系结构

JVM规范定义了一系列子系统以及它们的外部行为。JVM主要有以下子系统:

  • Class Loader 类加载器。 用于读入Java源代码并将类加载到数据区。
  • Execution Engine 执行引擎。 执行来自数据区的指令。

数据区使用的是底层操作系统分配给JVM的内存。

类加载器(Class Loader)

JVM在下面几种不同的层面使用不同的类加载器:

  • bootstrap class loader(引导类加载器):是其他类加载器的父类,它用于加载Java核心库,并且是唯一一个用本地代码编写的类加载器。
  • extension class loader(扩展类加载器):是bootstrap class loader加载器的子类,用于加载扩展库。
  • system class loader(系统类加载器):是extension class loader加载器的子类,用于加载在classpath中的应用程序的类文件。
  • user-defined class loader(用户定义的类加载器):是系统类加载器或其他用户定义的类加载器的子类。

当一个类加载器收到一个加载类的请求,首先它会检查缓存,确认该类是否已经被加载,然后把请求代理给它的父类。如果父类没能成功的加载类,那么子类就会自己去尝试加载该类。子类可检查父类加载器的缓存,但父类不能看到子类所加载的类。之所类加载体系会这样设计,是认为一个子类不应该重复加载已经被父类加载过的类。

执行引擎(Execution Engine)

执行引擎一个接一个地执行被加载到数据区的字节码。为了保证字节码指令对于机器来说是可读的,执行引擎使用下面两个方法:

  • 解释执行:执行引擎把它遇到的每一条指令解释为机器语言。
  • 即时编译:如果一条指令经常被使用,执行引擎会把它编译为本地代码并存储在缓存中。这样,所有和这个方法相关的代码都会直接执行,从而避免重复解释。

尽管即时编译比解释执行要占用更多的时间,但是对于需要使用成千上万次的方法,只需要处理一次。相比每次都解释执行,以本地代码的方式运行会节约很多执行时间。

JVM规范中并不规定一定要使用即时编译。即时编译也不是用于提高JVM性能的唯一的手段。规范仅仅规定了每条字节码对应的本地代码,至于执行引擎如何实现这一对应过程的,完全由JVM的具体实现来决定。

内存模型(Memory Model)

Java内存模型建立在自动内存管理的概念之上。当一个对象不再被一个应用所引用,垃圾回收器就会回收它,从而释放相应的内存。这一点和其他很多需要自行释放内存的语言有很大不同。

JVM从底层操作系统中分配内存,并将它们分为以下几个区域:

  • 堆空间(Heap Space):这是共享的内存区域,用于存储可以被垃圾回收器回收的对象。
  • 方法区(Method Area):这块区域以前被称作“永生代”(permanent generation),用于存储被加载的类。这块区域最近被JVM取消了。现在,被加载的类作为元数据加载到底层操作系统的本地内存区。
  • 本地区(Native Area):这个区域用于存储基本类型的引用和变量。

一个有效的管理内存方法是把对空间划分为不同代,这样垃圾回收器就不用扫描整个堆区。大多数的对象的生命周期都很段短暂,那些生命周期较长的对象往往直到应用退出才需要被清除。

当一个Java应用创建了一个对象,这个对象是被存储到“初生池”(eden pool)。一旦初生池存储满了,就会在新生代触发一次minor gc(小范围的垃圾回收)。首先,垃圾回收器会标记出那些“死对象”(不再被应用所引用的对象),同时延长所有保留对象的生命周期(这个生命周期长度是用数字来描述,代表了期所经历过的垃圾回收的次数)。然后,垃圾回收器会回收这些死对象,并把剩余的活着的对象移动到“幸存池”(survivor pool),从而清空初生池。

当一个对象存活达到一定的周期后,它就会被移动到堆中的老生代:“终身代”(tenured pool)。最后,当终身代被填满时,就会触发一次full gc或major gc(完全的垃圾回收),以清理终身代。

(译者注:一般我们把初生池和幸存池所在的区域合并成为新生代,把终身代所在的区域成为老生代。对应的,在新生代上产生的gc称为minor gc,在老生代上产生的gc称为full gc。希望这样大家在其他地方看到对应的术语时能更好理解)

当垃圾回收(gc)执行的时候,所有应用线程都要被停止,系统产生一次暂停。minor gc非常频繁,所以被优化的能够快速的回收死对象,是新生代的内存的主要的回收方式。major gc运行起来就相对慢得多,因为要扫描非常多的活着的对象。垃圾回收器本身也有多种实现,有些垃圾回收器在一定情况下能更快的执行major gc。

堆的大小是动态的,只有堆需要扩张的时候才会从内存中分配。当堆被填满时,JVM会重新给堆分配更多的内存,直到达到堆大小的上限,这种重新分配同样会导致应用的短暂停止。

线程

JVM是运行在一个独立的进程中的,但它可以并发执行多个线程,每个线程都运行自己的方法,这是Java必备的一个部分。以即时消息客户端这样一个应用为例,它至少运行两个线程。一个线程用于等待用户输入,另一个检查服务端是否有新的消息传输。再以服务端应用为例,有时一个请求可能要涉及多个线程并发执行,所以需要多线程来处理请求。

在JVM的进程中,所有的线程共享内存和其他可用的资源。每一个JVM进程在进入点(main方法)处都要启动一个主线程,其他线程都从主线程启动,成为执行过程中的一个独立部分。线程可以再不同的处理器上并行执行,同样也可以共享一个处理器,线程调度器负责处理多个线程共享一个处理器的情况。

很多应用(特别是服务端应用)会处理很多任务,需要并行运行。这些任务中有些是非常重要的,需要实时执行的。而另外一些是后台任务,可以在CPU空闲时执行。任务是在不同的线程中运行的。举例子来说,服务端可能有一些低优先级的线程,它们会根据一些数据来计算统计信息。同时也会启动一些高优先级的进程用于处理传入的数据,响应对这些统计信息的请求。这里可能有很多的源数据,很多来自客户端的数据请求,每个请求都会使服务端短暂的停止后台计算的线程以响应这个请求。所以,你必须监控在运行的线程数目并且保证有足够的CPU时间来执行必要的计算。

(译者注:这一段在原文中是在性能优化的章节,译者认为这可能是作者的不小心,似乎放在线程的章节更合适。)

性能优化

JVM的性能取决于其配置是否与应用的功能相匹配。尽管垃圾回收器和内存回收进程是自动管理内存的,但是你必须掌管它们的频率。通常来说,你的应用可使用的内存越多,那么这些会导致应用暂停的内存管理进程需要起作用的就越少。

如果垃圾回收发生的频率比你想的要多很多,那么可以在启动JVM的时候为其配置更大的最大堆大小值。堆被填满的时间越久,就越能降低垃圾回收发生的频率。最大堆大小值可以在启动JVM的时候,用-Xmx参数来设定。默认的最大堆大小是被设置为可用的操作系统内存的四分之一,或者最小1GB。

如果问题出在经常重新分配内存,那么你可以把初始化堆大小设置为和最大堆大小一样。这就意味着JVM永远不需要为堆重新分配内存。但这样做就会失去动态堆大小适配的优化,堆的大小从一开始就被固定下来。配置初始化对大小是在启动JVM,用-Xms来设定。默认初始化堆大小会被设定为操作系统可用的物理内存的六十四分之一,或者设置一个最小值。这个值是根据不同的平台来确定的。

如果你清楚是哪种垃圾回收(minor gc或major gc)导致了性能问题,可以在不改变整个堆大小的情况下设定新生代和老生代的大小比例。对于需要产生大量临时对象的应用,需要增大新生代的比例(当然,后果是减小了老生代的大小)。对于长生命周期对象较多的应用,则需增大老生代的比例(自然需要减少新生代的大小)。以下几种方法可以用来设定新生代和老生代的大小:

  • 在启动JVM时,使用-XX:NewRatio参数来具体指定新生代和老生代的大小比例。比如,如果想让老生代的大小是新生代的五倍,则设置参数为-XX:NewRatio=5,默认这个参数设定为2(即老生代占用堆空间的三分之二,新生代占用三分之一)。
  • 在启动JVM时,直接使用-Xmn参数设定初始化和最大新生代大小,那么堆中的剩余大小即是老生代的大小。
  • 在启动JVM时,直接使用-XX:NewSize-XX:MaxNewSize参数设定初始化和最大新生代大小,那么堆中的剩余大小即是老生代的大小。

每一个线程都有一个栈,用于保存函数调用、返回地址等等,这些栈有着对应的内存分配。如果线程过多,就会导致OutOfMemory错误。即使你有足够的空间的堆来存放对象,你的应用也可能会因为创建一个新的线程而崩溃。这种情况下,需要考虑限制线程中的栈大小的最大值。线程栈大小可以在JVM启动的时候,通过-Xss参数来设置,默认这个值被设定为320KB至1024KB之间,这和平台相关。

性能监控

当开发或运行一个Java应用的时候,对JVM的性能进行监控是很重要的。配置JVM不是一次配置就万事大吉的,特别是你要应对的是Java服务器应用的情况。你必须持续的检查堆内存和非堆内存的分配和使用情况,线程数的创建情况和内存中加载的类的数据情况等。这些都是核心参数。

使用Anturis控制台,你可以为任何的硬件组件上运行的JVM配置监控(例如,在一台电脑上运行的一个Tomcat网页服务器)。

JVM监控可以使用以下衡量标准:

  • 总内存使用情况(MB):即JVM使用的总内存。如果JVM使用了所有可用内存,这项指标可以衡量底层操作系统的整体性能。
  • 堆内存使用(MB):即JVM为运行的Java应用所使用的对象分配的所有内存。不使用的对象通常会被垃圾回收器从堆中移除。所以,如果这个指数增大,表示你的应用没有把不使用的对象移除或者你需要更好的配置垃圾回收器的参数。
  • 非堆内存的使用(MB):即为方法区和代码缓存分配的所有内存。方法区是用于存储被加载的类的引用,如果这些引用没有被适当的清理,永生代池会在每次应用被重新部署的时候都会增大,导致非堆的内存泄露。这个指标也可能指示了线程创建的泄露。
  • 池内总内存(MB):即JVM所分配的所有变量内存池的内存和(即除了代码缓存区外的所有内存和)。这个指标能够让你明确你的应用在JVM过载前所能使用的总内存。
  • 线程:即所有有效线程数。举个例子,在Tomcat服务器中每个请求都是一个独立的线程来处理,所以这个衡量指标可以表示当前有多少个请求数,是否影响到了后台低权限的线程的运行。
  • 类:即所有被加载的类的总数。如果你的应用动态的创建很多类,这可能是服务器内存泄露的一个原因。

 

分享到:
评论

相关推荐

    JVM内存回收权威指南

    JVM内存回收最全的手册书籍,详细介绍了各种内存回收算法,是Java程序员进阶的必备书籍

    Java初学者必备指南,值得收藏.pdf

    它的设计原则是面向对象、基于类,且具有高度的可移植性,这得益于Java虚拟机(JVM)。JVM允许Java程序在任何支持Java的平台上运行,无需重新编译。Java运行时环境(JRE)包含了运行Java程序所需的基本组件,而Java...

    jvm学习,面试,性能优化等等

    通过阅读《JVM常见面试题指南》、《JVM面试专题》、《JVM面试题》等资料,可以深入理解和掌握这些知识点,并在面试中脱颖而出。同时,《JVM执行子系统》和《JVM性能优化相关问题》等文档将帮助我们更全面地了解JVM的...

    Programming.Concurrency.on.the.JVM

    《编程并发在JVM上》是一部全面而深入的指南,它不仅涵盖了JVM环境下的多线程编程基础知识,还深入探讨了最新的并发模型和工具。对于希望在JVM平台上提升并发编程能力的开发者而言,这本书无疑是通往精通之路的重要...

    jvm参数调优-jvmSample.zip

    《JVM参数调优——深度解析与实践指南》 在Java开发中,JVM(Java Virtual Machine)扮演着至关重要的角色。它不仅负责执行Java代码,还管理内存、线程等资源,确保程序的高效运行。然而,如果不合理地配置JVM参数...

    java面试指南包括 Java基础、Java并发、JVM、MySQL、Redis、Spring、MyBatis、Kafka

    Java面试指南涵盖了广泛的IT技术领域,对于准备Java相关职位的求职者来说,这些知识点是必不可少的。下面将分别解析每个领域的关键概念。 1. **Java基础**:Java是一种跨平台的面向对象编程语言,它的核心特性包括...

    java面试指南包括 Java基础、Java并发、JVM、MySQL、Redis、Spring、MyBatis、Kafka等

    作为常用的关系型数据库,MySQL的查询语句、事务处理、索引优化、存储引擎等方面的知识是必备的。面试者需要熟悉SQL语言,理解ACID特性,以及如何通过调整SQL来提升查询效率。 五、Redis Redis是一款高性能的内存...

    《Java架构师指南》 配套代码

    在Java开发中,设计模式是架构师必备的知识,如单例模式、工厂模式、观察者模式等,它们是解决常见问题的标准解决方案,能够提高代码的可读性和可维护性。书中的代码示例将帮助读者更好地理解和应用这些模式。 并发...

    Java2学习指南2.pdf

    学习如何使用这些工具包来设计用户友好的界面是开发桌面应用的必备技能。 2. **Servlets与JSP**:对于Web开发,Java提供了Servlets和JavaServer Pages(JSP)技术,它们用于生成动态网页内容。掌握Servlets和JSP...

    Java高级开发指南

    7. **JVM内部机制**:了解JVM的工作原理,包括类加载、内存管理(垃圾回收)、JIT编译和性能调优,对于写出高效、稳定的Java应用非常有帮助。 8. **泛型**:Java泛型为集合和其他容器提供了类型安全,避免了强制...

    深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)1

    【深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)1】这本书是Java开发者必备的参考资料,详细探讨了Java虚拟机(JVM)的高级特性及其最佳实践。作者周志明通过深入浅出的方式,引领读者走进Java的世界,了解其...

    Java学习、面试必备

    Java是一种广泛使用的面向...总之,"新手必看"这个压缩包为Java初学者提供了一个全面的学习和面试指南,涵盖了从基础到进阶的各个层面,是提升Java技能的宝贵资源。通过系统学习和不断实践,你将在Java世界中游刃有余。

    JAVA性能优化权威指南

    总的来说,《JAVA性能优化权威指南》全面覆盖了从JVM配置、代码优化、数据库优化、网络I/O到监控工具的各个方面,是Java开发者进行性能优化的必备读物。通过深入学习这本书,开发者不仅可以提升个人技能,还能有效...

    【Java面试+Java后端技术学习指南】:一份通向理想互联网公司的面试指南,包括 Java,技术面试必备基础知识、.zip

    【Java面试+Java后端技术学习指南】是一份详尽的资源,旨在帮助开发者准备Java后端技术面试,以及提供通向理想互联网公司的学习路径。这份资料可能包含多个部分,如Java基础知识、数据结构与算法、并发编程、框架...

    java高级进阶知识

    《深入理解Java虚拟机:JVM高级特性与最佳实践》是Java开发者深入探索JVM的必备书籍,尤其对于想要在Java领域进一步提升的程序员来说,它提供了丰富的理论基础和实践经验。这本书的第二版更是对原有的内容进行了更新...

    通俗易懂、风趣幽默的Java学习指南

    此外,了解数据库连接池(如C3P0、HikariCP)和事务管理也是必备技能。 五、Java面试 Java面试常见问题涵盖了上述所有知识点,还会涉及到设计模式、算法、异常处理、集合框架深入理解等。例如,设计模式如单例、...

    JAVA入门必备手册

    "JAVA入门必备手册"很可能包含了一系列关于Java编程的基础概念和实践指南,旨在帮助初学者快速掌握这门语言。 首先,手册可能介绍了Java的基本语法,包括变量、数据类型、运算符、流程控制语句(如if-else、for、...

    Java程序员面试必备知识点总结

    内容概要:本文涵盖了一系列重要的Java基础知识与进阶概念的面试问题及其详细解答,其中包括JVM的工作原理,垃圾回收的实施细节,多线程的解决方案,以及诸如单例设计模式、异常处理和线程安全集合等实际应用中的...

    eclipse生成exe必备

    使用这些工具和指南,开发者可以有效地将Eclipse中的Java项目转化为Windows用户友好的.exe程序,从而拓宽了应用程序的受众群体。 总结起来,Eclipse生成.exe文件的过程涉及的关键步骤包括:使用Eclipse导出JAR,...

    架构师成长必备宝典

    本指南将深入探讨与Java相关的架构设计原则、最佳实践和技术趋势。 首先,作为Java架构师,你需要掌握核心的Java编程语言,包括面向对象设计原则,如单一职责、开放封闭、里氏替换、依赖倒置和接口隔离等。理解这些...

Global site tag (gtag.js) - Google Analytics