Java虚拟机技术总结(07年写的

Posted on

Java虚拟机技术总结(07年写的,原JavaEye精华帖)

原文:IBM WebSphere Application Server 诊断和调优(一)) 大家可以google:“IBM WebSphere Application Server 诊断和调优”。 近段时间,我们项目中用到的WebSphere应用服务器(WAS),但在客户的production环境下极不稳定,经常宕机。给客户造成非常不好的影响,同时,也给项目组很大压力。为此,我们花了近一个月时间对其诊断,现在基本上稳定了,需要继续观察一段时间。现在我主要将工作做一个阶段性的总结。 我们的产品环境是:WAS6.0+DB2 8.1+AIX5.3+RS/6000。在该产品环境下,出现的问题非常多,现象如下: WAS经常不稳定、宕机几乎一天一次,经常报告OutOfMemory(内存泄漏吗?NO)。 DB2连接数过大,有时把DB2撑死,有时也把AIX撑死。 AIX虚拟内存报错、分页报错、IO也报错、还有很多其它莫名奇妙的错。 总是,每次问题发生的现象和理论上的总是不一致,导致我们不知道从何入手,也无从检测自己的优化参数。咨询过多次IBM技术支持,只解决了某些局部问题。 虽然问题依然存在,但我想,解决问题的思路、特别是理论基础,还是有一些规律和原则。 对于WAS这块,我近段时间的主要时间集中在以下几个方面(时间顺序): 1、Java性能监测工具:Jprofiler,也用到Jprobe。后来发现Jprofiler在AIX下几乎不可用。 2、IBM Java虚拟机和WAS技术细节,特别是IBM JVM的GC原理,我发现它和sun、bea的差别很大。 3、IBM的heap分析器Heap Analyzer、GCCollector。这两个事后监测工具非常实用,特别是我们的产品运行环境,非测试环境。 4、某些Application的怀疑和诊断。 5、AIX诊断,我几乎没有这个能力,只能常规监测一下,需另请高人。 我打算将本文分成以下几个部分总结: JVM原理、IBM JVM的GC策略和调优 Jprofiler和IBM工具的实际体会 WAS的诊断体会和AIX调优 下面开始主题吧,可能比较零碎,另外,开始的理论篇基本上看书都可以,我只是总结一下,再添加一些自己的理解。 以下是我参考的最重要的两本电子书和一些网站: 《Inside Java Vrtual Machine》:半部分有约四章我认为非常棒,其它章节可能意义不大。 《The Java Virtual Machine Specification, 2nd》:前半部分有两三章很不错,不过可以对照上一本书看。 sun的hotspot虚拟机技术:[url]http://java.sun.com/javase/technologies/hotspot/ [/url] BEA的JRockit虚拟机技术:[url]http://edocs.bea.com/jrockit/geninfo/genintro/index.html [/url]JVM技术文档入口,虚拟机理论,内存泄漏诊断等的索引页。 IBM诊断资料:http://www-128.ibm.com/developerworks/java/jdk/diagnosis/ 上面有一个500多页的pdf文档,对IBM JVM技术和诊断讲解很深入。 我不得不提的是,在查资料这块,BEA和Sun都有很好的官方文档和论坛支持,并且官方文档导航非常好。虽然IBM的诊断资料也不少,但需要搜索,其搜索是很痛苦的。而且,IBM官方论坛很差。如果用IBM的产品出问题,切记:找IBM技术支持,千万不要蒙着头搞!反正它们的产品很少免费。说实话,它们的技术支持还是挺负责的,一般会为你推荐很多support资料,而该资料往往都在developerworks网站上,属于support那个频道,但你就是搜不着。 Java虚拟机规范概要 研究Java虚拟机,首先要了解Sun的Java虚拟机规范。现在,该实现版本很多,如比较有名的Sun、IBM、BEA、Apple、HP、MS、Apache Harmony。它们都实现了JVM规范,但有各自扩展。譬如,针对IBM虚拟机的堆碎片导致OutOfMemory(OOM),在Sun的虚拟机上就不会发生。Sun的JVM有maxPermSize的概念,IBM就没有,如果你设置这个参数,虚拟机根本就启动不了。 比较有意思的是,学Java,就一定要了解各种规范,这和MS的风格很不一样。Sun总是在定义一些规范,实现都留给各厂商。我们除了理解规范本身外,一定要理解规范和实现之间的关系,譬如JDBC规范和JDBC驱动的关系,它们是怎么组合到一起的。要是你用过php的xml解析库,或db函数,就会体会深刻,它们可没有什么规范可言,所以每个数据库厂商的db函数用法都不一样。我推荐大家研读一下HSQLDB的jdbc和Tomcat的servlet相关实现,因为我认为它们还是比较好懂的。 JVM规范只是定义一个虚拟机该做什么,但它并没有要求你该怎么做。例如我们最常见的Servlet规范,在该规范中,有HttpServletRequest、HttpServletResponse,HttpSession等接口,但它们的实现都留给了各个容器厂商。遗憾的是,规范留下的空白,会把我们这些开发人员给整惨了:容器间移植有时候就是恶梦。譬如J2EE并没有SSO规范,但它很重要,我以前专门针对它做过WebSphere AppServer和Weblogic AppServer的SSO项目,差别还是不小,不过还是有点共通,那就是都遵循JAAS规范。 JVM的结构 从功能上分,Java虚拟机主要由六个部分组成,可以分成三类: 第一类:JVM API:就是我们最常用的Java API,它是开发人员和Java交互的入口,它主要是JAVA_HOME/jre/lib下的运行时类库rt.jar和编译相关的tools.jar 第二类:JVM内部组件 类装载器(ClassLoader):将Byte Array的 .class文件装载、链接和初始化。 内存管理(Memory Managent):为对象分配内存,以及释放内存。后者就是垃圾回收Garbage Collector(GC)。由于JVM最复杂的、最影响性能的就是GC,所以内存管理一般就指垃圾回收。 诊断接口(Diagostics Interface):这主要体现在JVMTI(jdk1.4下的JVMPI和JVMDI),它主要用来诊断程序的问题和性能,一般提供给工具厂商实现。如eclispe IDE下的debug功能,Jprofiler性能调优工具。 类解释器(Interpreter):解释装载进虚拟机的class对象,包括JIT等特性相关。 第三类:平台相关接口(Platform Interface):主要为了跨操作系统平台重用JVM代码,不过,它和我们开发人员关系不大。 在以上六个组件中,我们开发人员最关心的是ClassLoader和GC,用Java做系统框架、容器和它们密切相关。做业务系统时一些基础代码也和它们打交道,譬如最常用的Class.forName(),Thread.currentThread.getContextClassLoader()。我们仔细想想,为什么是上面两个问题?因为,它和我们class的整个生命周期最为相关:怎么将一个class和相关class加载进来,class实例什么时候创建,什么时候被销毁? 所以,下面的部分我们要专门讨论这些问题。 ClassLoader JVM主要有三类ClassLoader:Bootstrap、Extention、Application,该三类ClassLoader从上到下是分级(hierarchy)结构,遵循代理模型(Delegation Model)。 Tip:大家可以看看sun.misc.Launcher的源码,Bootstrap和Extention就在该文件里。该src可以在sun的网站上下载该压缩包,约60M(jdk-1_5_0-src-scsl.zip),它不在jdk自带的那个src.zip里。 Bootstrap ClassLoader:也称为primordial(root) class loader。主要是负责装载jre/lib下的jar文件,当然,你也可以通过-Xbootclasspath参数定义。该ClassLoader不能被Java代码实例化,因为它是JVM本身的一部分。 Extention ClassLoader:该ClassLoader是Bootstrap classLoader的子class loader。它主要负责加载jre/lib/ext/下的所有jar文件。只要jar包放置这个位置,就会被虚拟机加载。一个常见的、类似的问题是,你将mysql的低版本驱动不小心放置在这儿,但你的Web应用程序的lib下有一个新的jdbc驱动,但怎么都报错,譬如不支持JDBC2.0的DataSource,这时你就要当心你的新jdbc可能并没有被加载。这就是ClassLoader的delegate现象。常见的有log4j、common-log、dbcp会出现问题,因为它们很容易被人塞到这个ext目录,或是Tomcat下的common/lib目录。 Application ClassLoader:也称为System ClassLoaer。它负责加载CLASSPATH环境变量下的classes。缺省情况下,它是用户创建的任何ClassLoader的父ClassLoader,我们创建的standalone应用的main class缺省情况下也是由它加载(通过Thread.currentThread().getContextClassLoader()查看)。 我们实际开发中,用ClassLoader更多时候是用其加载classpath下的资源,特别是配置文件,如ClassLoader.getResource(),比FileInputStream直接。 ClassLoader是一种分级(hierarchy)的代理(delegation)模型。 Delegation:其实是Parent Delegation,当需要加载一个class时,当前线程的ClassLoader首先会将请求代理到其父classLoader,递归向上,如果该class已经被父classLoader加载,那么直接拿来用,譬如典型的ArrayList,它最终由Bootstrap ClassLoader加载。并且,每个ClassLoader只有一个父ClassLoader。 Class查找的位置和顺序依次是:Cache、parent、self。 Hierarchy:上面的delegation已经暗示了一种分级结构,同时它也说明:一个ClassLoader只能看到被它自己加载的classes,或是看到其父(parent) ClassLoader或祖先(ancestor) ClassLoader加载的Classes。 在一个单虚拟机环境下,标识一个类有两个因素:class的全路径名、该类的ClassLoader。 我碰到的一个典型的例子是:在做WAS的SSO开发时,由于我们的类是由WAS在启动时加载,该ClassLoader比下面的部署的Applicaton的ClassLoader的级别高。所以,在我们自己的类中没法用到应用程序的连接池,必须自建。 代理模型是Java安全模型的保证。譬如,我们自己写一个String.java,并且编译、package到自己的java.lang包下。按照代理模型,当前线程的ClassLoader会将其代理到父ClassLoader,父ClassLoader(最终会是Bootstrap)会找到rt.jar下的String.class,也就是说我们的String.class不会捣乱。 自定义ClassLoader 我们前面说过,自定义ClassLoader的缺省父ClassLoader是Application ClassLoader。一般的应用开发用不到它,但我们最好理解。因为在内存泄漏查找、应用程序部署出问题时,很多都和它有关。 譬如,内存泄漏是怎么产生的?这就涉及到ClassLoader和Class的生命周期。我曾经碰到这样一个问题:我们的程序用到了Webwork和Spring框架,当部署到Tomcat下时没有任何问题,但部署到WAS下,报告找不到Webwork的xml的DTD文件,而且Spring的日志也总是失效。Why?因为解析xml dtd时,用的是IBM的Xerces,不是我们的。而Spring日志问题是因为应用程序用的是WAS的Common-log.jar,而不是我们的。将应用的ClassLoader从默认的Parent-First,改成Parent-Last就可以解决,不过我们项目中用到其它库,又发生了其它问题。 一般来说,用到自定义ClassLoader有三种情况: 1、应用框架可以自己控制Classes的目录,并且自动部署。 我读过Jive公司的Wildfire(著名的即时通讯服务器),它自己有一套应用框架,非常灵活,遵循该框架插件规范的的第三方的plug-in放置在指定目录可以自动部署,实现某些扩展功能,如文件传输、语音聊天。 2、区分用户代码 这被广泛应用在Servlet容器和类似容器,譬如EJB Container设计中,大家看到Tomcat下有common、server、share三个目录吧(ClassLoader顺序从左到有),另外也有用户应用的WEB-INF目录,它是我们自己开发的。 3、允许Classes卸载 如果没有自定义的ClassLoader,那么我们自己应用中的classes永远都不能被卸载,因为这些类被Application ClassLoader加载后cache起来了,我们的classes一直对该ClassLoader有引用,而该系统级的ClassLoader永远都不会被卸载,除非JVM shutdown了。JSP和Servlet的动态部署就用到这个特性。 待续……. Note: 还有JVM运行时(Runtime)架构,ClassLoader加载class过程没有总结,这两部分我觉得太重要了,但内容太多,写不完啊。 这部分内容,《Inside Java Virtual Machine》讲解非常清楚,BEA的官方网站这部分也非常不错,要理解深刻,我建议结合JProfiler工具,非常直观。 待续……. 该文仅存的一点回复: [zwchen] 为什么都说WAS难用? 安装过程 1、先安装WAS,然后创建三种不同类型的profiles(manager,default,custom),譬如缺省的profiles吧,在创建这个profiles过程中,需要启动至少10个端口,要是那10个默认端口有些被你的系统占用了,麻烦就来了。改端口不就ok了吗?但你知道怎么改吗?可能你觉得没有必要10个端口,但WAS就是需要这么多:控制台端口两个(http,https),引导端口2089,soap、ORB,单元 cell发现…. 总之,你都不知道你为什么要关心这么多端口。WLS够绝了:一个smart端口 7001全部搞定,在上走各种协议。 2、系统为你生成的上百个bat命令文件,里面交叉引用的path都是绝对路径,所以,往往你的机器重装了,那么WAS也重装吧。WLS和JBoss可以将整个文件夹移动,WAS不行,如果你喜欢折腾,当然也可以做到,就看值不值了。 控制台的部署过程 1、它总是以为我们的应用前台是httpd Server分发,于是让我们选择虚拟主机。其实,我们不是CMS系统,我们的Web前端就是WAS的web容器。 2、它总是以为我们的应用包含EJB,或是要发布成Web Services,于是让我们选择进退两难。其实,我们只是简单的Web应用。 3、它总是以为我们的应用要部署分散在多个WAS上,或者在cluster里,所以会强制要求我们选择发布目标位置。其实,我们只是用单Server。 4、它以为我们发布的应用是EAR企业包,而WAR包只是一个Web前端展示模块,所以它总是在xml配置里面写一堆。其实,我们很想手动修改配置文件。 我刚才特别部署了一个简单的web应用,就是Struts的sample,写入了29个xml配置文件。设想,保存部署文件是一个事务性的过程,如果你在大连,服务器在北京,速度超级慢,这个写入文件过程失败,呵呵,你就下地狱吧,我们碰到个几次,有一次就被迫服务器profiles重建。 虽然上面很多步骤我省略了,对于我特别说到的,虽然有默认,但每个默认页有上10个选项,而你并不是很明白这些选择的确切含义,它们是否相互冲突。 我建议WAS的设计师:请留下尽量少的发布步骤,需要定制,可以部署后修改,学学WLS吧:考虑普通开发人员的感受。 开发过程 用WAS,最好有其配套的开发工具,eclipse那个WAS插件,我试过不太好用。用RAD吧,要lisence。RAD也是个超级大户,官方推荐内存是最低768M,WAS官方推荐大概是1000M,我的1G机器也可以跑,就是慢了点。 大家如果用WAS的EJB容器,并且你想做standalone的ejb客户端,那么,千万不要选择IBM之外的JVM,我固执地试过,最后用sun的JVM成功了,但需要一堆IBM WAS下的jar,而且必须走IIOP协议,我可以说,初次使用99%的尝试是失败的。而且,此时的开发工具一定要选择IBM的 RAD或WSAD。建议,学习EJB这类分布式开发阶段,用WLS或JBoss吧,因为它们的EJB standalone客户端都支持smart stub。譬如,对于JBoss,它会通过socket先将客户端stub下载到本地,你察觉不到这个过程。大概WLS是通过反射生成stub。而WAS 必须部署过程编译一堆stub,需要引用一堆jar,sun的ejb部署大概也这样。之所以sun的简单点,那是因为你的ejb客户端jar往往自动被它加入了classpath,或者客户端是在AppServer里。 如果你要开发基于WAS的Web Services,并且用WAS内置的Web Services引擎,那么你一定要用它自己的开发工具发布和部署,我虽然成功了,只是因为太固执,付出的代价太大。 问题诊断 用WAS出问题比不出问题正常。出问题咋办?这个我暂时不说了,留给我的后文吧,总之一句话:问题独一无二。 我本人并不是说WAS做得烂,我只是想说明一点,它的使用,对我们的理论技术水平要求太高,特别不适合初学者。 ———————————————————- 抛出异常的爱 写道 用的是5.只有一本。。。中文的。。 6有没有中文的红宝书? [zwchen]像是现在还没有,我建议看它的原著吧,非常易懂,图特多,一天看200多页应该不困难。学习WAS,往往一本红皮书是不够的。市面上介绍WAS的书,大概极少有超越红皮书的。 ——————————————————– [zwchen]惭愧啊,我确实算不上高手,真正的高手在北京的IBM中国技术支持中心。我和那边的人电话和邮件聊过好多次,那才是一个字:高! 了解WAS,我随便总结一下吧: 引用

前提:你是项目组专门负责WAS上开发、部署、调优的,并且有与WAS抗争的决心。 1、学习WAS宝典丛书:WAS相关红皮书,前面我介绍过几本。并且多研究研究WAS的目录结构。 2、一本非红皮书,IBM内部的技术支持汇总,主要是关于诊断,但它最难吃透但最受用:《IBM® Developer Kit and Runtime Environment, Java™ 2 Technology Edition, Version 6.0 Diagnostics Guide》,500多页。 3、一定要有JVM相关技术积累:ClassLoader、GC策略,而且一定要注意它们与Sun的JVM的区别,往往WAS的问题发生在这个上面。如Sun的JVM一般建议heap的最大最小值一样,但IBM的JVM你要是这么做会导致严重的碎片问题。Why?默认的GC策略不同。 4、用JProfiler这类工具深入到WAS内部。 5、最好对JavaEE相关技术的原理有较深入的了解,譬如EJB的原理、Servlet的原理。而且,最好是这些技术怎么实现的,譬如读读Tomcat的源码,我强烈建议大家读一下这篇文章:http://www.onjava.com/pub/a/onjava/2003/05/14 /java_webserver.html。 整体体会,WAS的功底是在WAS之外,它只是对JVM、JavaEE规范的一个实现罢了。 另外也建议:不要花太多的时间的在WAS上,我认为非常不值。想研究,还不如去看JBoss,WLS。WAS这东西,知道一般的用法就够了,而且你永远都不可能明白它为什么有那么多的bug和不合理,明白了又能咋样?你的时间都白白浪费了。

eden区没有发生minor gc,对象直接分配在了old gen

Posted on

eden区没有发生minor gc,对象直接分配在了old gen

您还未登录 ! 登录 注册

ITeye3.0

群组首页编程语言高级语言虚拟机论坛

发表回复

[讨论] eden区没有发生minor gc,对象直接分配在了old gen

等待雨季的到来的博客 等待雨季的到来 2013-07-21

我的测试代码如下 Java代码 复制代码 收藏代码

  1. ///
  2. /* -Xmx100M -XX:+PrintGCDetails -XX:MaxNewSize=40M -XX:+PrintTenuringDistribution
  3. /*/
  4. public static void testAllocation(){
  5. byte[] byte1 = new byte[_1MB/*5];
  6. byte[] byte2 = new byte[_1MB/*10];
  7. byte1 = null;
  8. byte2 = null;
  9. byte[] byte3 = new byte[_1MB/*5];
  10. byte[] byte4 = new byte[_1MB/*10];
  11. byte3 = null;
  12. byte4 = null;
  13. byte[] byte5 = new byte[_1MB/*15];
  14. }
    /// / -Xmx100M -XX:+PrintGCDetails -XX:MaxNewSize=40M -XX:+PrintTenuringDistribution // public static void testAllocation(){ byte[] byte1 = new byte[_1MB/5]; byte[] byte2 = new byte[_1MB/10]; byte1 = null; byte2 = null; byte[] byte3 = new byte[_1MB/5]; byte[] byte4 = new byte[_1MB/10]; byte3 = null; byte4 = null; byte[] byte5 = new byte[_1MB/*15]; } 因为没有用NewSize限定新生代的初始大小,所以eden区的初始大小为20M,而我用MaxNewSize限定了新生代的初始大小为40M。 GC Log Java代码 复制代码 收藏代码

  15. [GC

  16. Desired survivor size 3538944 bytes, new threshold 7 (max 15)
  17. [PSYoungGen: 16194K->368K(24320K)] 16194K->368K(79872K), 0.0020460 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
  18. Heap
  19. PSYoungGen total 24320K, used 17199K [0x0000000115490000, 0x0000000117c90000, 0x0000000117c90000)
  20. eden space 20864K, 80% used [0x0000000115490000,0x00000001164ffe38,0x00000001168f0000)
  21. from space 3456K, 10% used [0x00000001168f0000,0x000000011694c010,0x0000000116c50000)
  22. to space 3456K, 0% used [0x0000000117930000,0x0000000117930000,0x0000000117c90000)
  23. ParOldGen total 55552K, used 15360K [0x0000000111890000, 0x0000000114ed0000, 0x0000000115490000)
  24. object space 55552K, 27% used [0x0000000111890000,0x0000000112790010,0x0000000114ed0000)
  25. PSPermGen total 21248K, used 2695K [0x000000010c690000, 0x000000010db50000, 0x0000000111890000)
  26. object space 21248K, 12% used [0x000000010c690000,0x000000010c931ef0,0x000000010db50000)
    [GC Desired survivor size 3538944 bytes, new threshold 7 (max 15) [PSYoungGen: 16194K->368K(24320K)] 16194K->368K(79872K), 0.0020460 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] Heap PSYoungGen total 24320K, used 17199K 0x0000000115490000, 0x0000000117c90000, 0x0000000117c90000) eden space 20864K, 80% used [0x0000000115490000,0x00000001164ffe38,0x00000001168f0000) from space 3456K, 10% used [0x00000001168f0000,0x000000011694c010,0x0000000116c50000) to space 3456K, 0% used [0x0000000117930000,0x0000000117930000,0x0000000117c90000) ParOldGen total 55552K, used 15360K [0x0000000111890000, 0x0000000114ed0000, 0x0000000115490000) object space 55552K, 27% used [0x0000000111890000,0x0000000112790010,0x0000000114ed0000) PSPermGen total 21248K, used 2695K [0x000000010c690000, 0x000000010db50000, 0x0000000111890000) object space 21248K, 12% used [0x000000010c690000,0x000000010c931ef0,0x000000010db50000) 现象: 当分配byte3时触发了一次minor gc,byte1和byte2都被回收了,可是我分配byte5的时候,却直接分配在了老生代,byte3和byte4依旧在eden区,而byte5直接被分配在了老生代。 问题: 1、byte5为什么没有触发minor gc而是直接分配在老生代,byte3和byte4为什么没有被回收? 2、eden区什么时候才会自动扩容到40M,当创建超过20M的对象时,都是直接分配在了老生,而创建大小超过老生代的对象时老生代会自动扩容。 其他: 我在用eclipse调试的时候发现,同样的测试代码在执行的时候输出的GC log居然会有很大的变化,有的时候eden区被初始化为8M有的时候初始化为20M。我工作在OS X下,JDK版本如下。 java version "1.7.0_25" Java(TM) SE Runtime Environment (build 1.7.0_25-b15) Java HotSpot(TM) 64-Bit Server VM (build 23.25-b01, mixed mode) Spinner [RednaxelaFX的博客 RednaxelaFX 2013-07-25

楼主是看了毕玄的实验之后自己动手测试么? 把楼主的代码弄成可执行的,我用的测试代码是: Java代码 复制代码 收藏代码

  1. public class PSAllocTest {
  2. private static final int _1KB = 1024;
  3. private static final int _1MB = _1KB /* 1024;
  4. ///
  5. /* -Xmx100M -XX:+PrintGCDetails -XX:MaxNewSize=40M -XX:+PrintTenuringDistribution
  6. /*/
  7. public static void testAllocation() {
  8. byte[] byte1 = new byte[_1MB/*5];
  9. byte[] byte2 = new byte[_1MB/*10];
  10. byte1 = null;
  11. byte2 = null;
  12. byte[] byte3 = new byte[_1MB/*5];
  13. byte[] byte4 = new byte[_1MB/*10];
  14. byte3 = null;
  15. byte4 = null;
  16. byte[] byte5 = new byte[_1MB/*15];
  17. }
  18. public static void main(String[] args) {
  19. testAllocation();
  20. }
  21. }
    public class PSAllocTest { private static final int _1KB = 1024; private static final int _1MB = _1KB / 1024; /// / -Xmx100M -XX:+PrintGCDetails -XX:MaxNewSize=40M -XX:+PrintTenuringDistribution // public static void testAllocation() { byte[] byte1 = new byte[_1MB/5]; byte[] byte2 = new byte[_1MB/10]; byte1 = null; byte2 = null; byte[] byte3 = new byte[_1MB/5]; byte[] byte4 = new byte[_1MB/10]; byte3 = null; byte4 = null; byte[] byte5 = new byte[_1MB/15]; } public static void main(String[] args) { testAllocation(); } } 我这边在JDK7u9 64-bit Server VM上跑看到的GC日志会有两次GC: Gc log代码 复制代码 收藏代码

  22. D:\test\pstest>java -Xmx100M -XX:+PrintGCDetails -XX:MaxNewSize=40M PSAllocTest

  23. [GC [PSYoungGen: 16002K->584K(18688K)] 16002K->584K(61376K), 0.0016857 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
  24. [GC [PSYoungGen: 16648K->576K(34752K)] 32008K->15936K(77440K), 0.0014163 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
  25. Heap
  26. PSYoungGen total 34752K, used 1218K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000)
  27. eden space 32128K, 2% used [0x00000000fd800000,0x00000000fd8a0ab0,0x00000000ff760000)
  28. from space 2624K, 21% used [0x00000000ff9f0000,0x00000000ffa80030,0x00000000ffc80000)
  29. to space 2624K, 0% used [0x00000000ff760000,0x00000000ff760000,0x00000000ff9f0000)
  30. ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000)
  31. object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000)
  32. PSPermGen total 21248K, used 2505K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000)
  33. object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c72638,0x00000000f5ec0000)
    D:\test\pstest>java -Xmx100M -XX:+PrintGCDetails -XX:MaxNewSize=40M PSAllocTest [GC [PSYoungGen: 16002K->584K(18688K)] 16002K->584K(61376K), 0.0016857 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC [PSYoungGen: 16648K->576K(34752K)] 32008K->15936K(77440K), 0.0014163 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap PSYoungGen total 34752K, used 1218K 0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000) eden space 32128K, 2% used [0x00000000fd800000,0x00000000fd8a0ab0,0x00000000ff760000) from space 2624K, 21% used [0x00000000ff9f0000,0x00000000ffa80030,0x00000000ffc80000) to space 2624K, 0% used [0x00000000ff760000,0x00000000ff760000,0x00000000ff9f0000) ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000) object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000) PSPermGen total 21248K, used 2505K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000) object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c72638,0x00000000f5ec0000) 第二次GC其实已经在第5个数组成功分配之后了,就楼主关心的部分看我这边看到的跟楼主实验看到的行为一致,只要关心日志里的第一次GC就好了。 如果加上额外参数的话,可以看到更多GC信息: Java代码 [复制代码 收藏代码

  34. D:\test\pstest>java -Xmx100M -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintTLAB -XX:MaxNewSize=40M -XX:+PrintTenuringDistribution PSAllocTest

  35. {Heap before GC invocations=1 (full 0):
  36. PSYoungGen total 18688K, used 16002K [0x00000000fd800000, 0x00000000fecd0000, 0x0000000100000000)
  37. eden space 16064K, 99% used [0x00000000fd800000,0x00000000fe7a0b60,0x00000000fe7b0000)
  38. from space 2624K, 0% used [0x00000000fea40000,0x00000000fea40000,0x00000000fecd0000)
  39. to space 2624K, 0% used [0x00000000fe7b0000,0x00000000fe7b0000,0x00000000fea40000)
  40. ParOldGen total 42688K, used 0K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000)
  41. object space 42688K, 0% used [0x00000000f9c00000,0x00000000f9c00000,0x00000000fc5b0000)
  42. PSPermGen total 21248K, used 2495K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000)
  43. object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c6fc80,0x00000000f5ec0000)
  44. TLAB: gc thread: 0x000000000028c000 [id: 10344] desired_size: 321KB slow allocs: 3 refill waste: 5232B alloc: 0.99998 16003KB refills: 2 waste 6.2% gc: 40912B slow: 40B fast: 0B
  45. TLAB totals: thrds: 1 refills: 2 max: 2 slow allocs: 3 max 3 waste: 6.2% gc: 40912B max: 40912B slow: 40B max: 40B fast: 0B max: 0B
  46. [GC
  47. Desired survivor size 2686976 bytes, new threshold 7 (max 15)
  48. [PSYoungGen: 16002K->640K(18688K)] 16002K->640K(61376K), 0.0015035 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
  49. Heap after GC invocations=1 (full 0):
  50. PSYoungGen total 18688K, used 640K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000)
  51. eden space 16064K, 0% used [0x00000000fd800000,0x00000000fd800000,0x00000000fe7b0000)
  52. from space 2624K, 24% used [0x00000000fe7b0000,0x00000000fe850030,0x00000000fea40000)
  53. to space 2624K, 0% used [0x00000000ff9f0000,0x00000000ff9f0000,0x00000000ffc80000)
  54. ParOldGen total 42688K, used 0K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000)
  55. object space 42688K, 0% used [0x00000000f9c00000,0x00000000f9c00000,0x00000000fc5b0000)
  56. PSPermGen total 21248K, used 2495K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000)
  57. object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c6fc80,0x00000000f5ec0000)
  58. }
  59. {Heap before GC invocations=2 (full 0):
  60. PSYoungGen total 18688K, used 16704K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000)
  61. eden space 16064K, 100% used [0x00000000fd800000,0x00000000fe7b0000,0x00000000fe7b0000)
  62. from space 2624K, 24% used [0x00000000fe7b0000,0x00000000fe850030,0x00000000fea40000)
  63. to space 2624K, 0% used [0x00000000ff9f0000,0x00000000ff9f0000,0x00000000ffc80000)
  64. ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000)
  65. object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000)
  66. PSPermGen total 21248K, used 2498K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000)
  67. object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c708a0,0x00000000f5ec0000)
  68. TLAB: gc thread: 0x00000000022a5800 [id: 10100] desired_size: 321KB slow allocs: 0 refill waste: 5136B alloc: 0.99998 16064KB refills: 1 waste 100.0% gc: 328952B slow: 0B fast: 0B
  69. TLAB: gc thread: 0x000000000229e800 [id: 3928] desired_size: 321KB slow allocs: 0 refill waste: 5136B alloc: 0.99998 16064KB refills: 1 waste 100.0% gc: 328984B slow: 0B fast: 0B
  70. TLAB totals: thrds: 2 refills: 2 max: 1 slow allocs: 0 max 0 waste: 100.0% gc: 657936B max: 328984B slow: 0B max: 0B fast: 0B max: 0B
  71. [GC
  72. Desired survivor size 2686976 bytes, new threshold 7 (max 15)
  73. [PSYoungGen: 16704K->520K(34752K)] 32064K->15880K(77440K), 0.0012669 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
  74. Heap after GC invocations=2 (full 0):
  75. PSYoungGen total 34752K, used 520K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000)
  76. eden space 32128K, 0% used [0x00000000fd800000,0x00000000fd800000,0x00000000ff760000)
  77. from space 2624K, 19% used [0x00000000ff9f0000,0x00000000ffa72020,0x00000000ffc80000)
  78. to space 2624K, 0% used [0x00000000ff760000,0x00000000ff760000,0x00000000ff9f0000)
  79. ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000)
  80. object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000)
  81. PSPermGen total 21248K, used 2498K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000)
  82. object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c708a0,0x00000000f5ec0000)
  83. }
  84. Heap
  85. PSYoungGen total 34752K, used 1162K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000)
  86. eden space 32128K, 2% used [0x00000000fd800000,0x00000000fd8a0ab0,0x00000000ff760000)
  87. from space 2624K, 19% used [0x00000000ff9f0000,0x00000000ffa72020,0x00000000ffc80000)
  88. to space 2624K, 0% used [0x00000000ff760000,0x00000000ff760000,0x00000000ff9f0000)
  89. ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000)
  90. object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000)
  91. PSPermGen total 21248K, used 2505K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000)
  92. object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c72638,0x00000000f5ec0000)
    D:\test\pstest>java -Xmx100M -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintTLAB -XX:MaxNewSize=40M -XX:+PrintTenuringDistribution PSAllocTest {Heap before GC invocations=1 (full 0): PSYoungGen total 18688K, used 16002K [0x00000000fd800000, 0x00000000fecd0000, 0x0000000100000000) eden space 16064K, 99% used [0x00000000fd800000,0x00000000fe7a0b60,0x00000000fe7b0000) from space 2624K, 0% used [0x00000000fea40000,0x00000000fea40000,0x00000000fecd0000) to space 2624K, 0% used [0x00000000fe7b0000,0x00000000fe7b0000,0x00000000fea40000) ParOldGen total 42688K, used 0K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000) object space 42688K, 0% used [0x00000000f9c00000,0x00000000f9c00000,0x00000000fc5b0000) PSPermGen total 21248K, used 2495K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000) object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c6fc80,0x00000000f5ec0000) TLAB: gc thread: 0x000000000028c000 [id: 10344] desired_size: 321KB slow allocs: 3 refill waste: 5232B alloc: 0.99998 16003KB refills: 2 waste 6.2% gc: 40912B slow: 40B fast: 0B TLAB totals: thrds: 1 refills: 2 max: 2 slow allocs: 3 max 3 waste: 6.2% gc: 40912B max: 40912B slow: 40B max: 40B fast: 0B max: 0B [GC Desired survivor size 2686976 bytes, new threshold 7 (max 15) [PSYoungGen: 16002K->640K(18688K)] 16002K->640K(61376K), 0.0015035 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap after GC invocations=1 (full 0): PSYoungGen total 18688K, used 640K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000) eden space 16064K, 0% used [0x00000000fd800000,0x00000000fd800000,0x00000000fe7b0000) from space 2624K, 24% used [0x00000000fe7b0000,0x00000000fe850030,0x00000000fea40000) to space 2624K, 0% used [0x00000000ff9f0000,0x00000000ff9f0000,0x00000000ffc80000) ParOldGen total 42688K, used 0K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000) object space 42688K, 0% used [0x00000000f9c00000,0x00000000f9c00000,0x00000000fc5b0000) PSPermGen total 21248K, used 2495K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000) object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c6fc80,0x00000000f5ec0000) } {Heap before GC invocations=2 (full 0): PSYoungGen total 18688K, used 16704K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000) eden space 16064K, 100% used [0x00000000fd800000,0x00000000fe7b0000,0x00000000fe7b0000) from space 2624K, 24% used [0x00000000fe7b0000,0x00000000fe850030,0x00000000fea40000) to space 2624K, 0% used [0x00000000ff9f0000,0x00000000ff9f0000,0x00000000ffc80000) ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000) object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000) PSPermGen total 21248K, used 2498K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000) object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c708a0,0x00000000f5ec0000) TLAB: gc thread: 0x00000000022a5800 [id: 10100] desired_size: 321KB slow allocs: 0 refill waste: 5136B alloc: 0.99998 16064KB refills: 1 waste 100.0% gc: 328952B slow: 0B fast: 0B TLAB: gc thread: 0x000000000229e800 [id: 3928] desired_size: 321KB slow allocs: 0 refill waste: 5136B alloc: 0.99998 16064KB refills: 1 waste 100.0% gc: 328984B slow: 0B fast: 0B TLAB totals: thrds: 2 refills: 2 max: 1 slow allocs: 0 max 0 waste: 100.0% gc: 657936B max: 328984B slow: 0B max: 0B fast: 0B max: 0B [GC Desired survivor size 2686976 bytes, new threshold 7 (max 15) [PSYoungGen: 16704K->520K(34752K)] 32064K->15880K(77440K), 0.0012669 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap after GC invocations=2 (full 0): PSYoungGen total 34752K, used 520K 0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000) eden space 32128K, 0% used [0x00000000fd800000,0x00000000fd800000,0x00000000ff760000) from space 2624K, 19% used [0x00000000ff9f0000,0x00000000ffa72020,0x00000000ffc80000) to space 2624K, 0% used [0x00000000ff760000,0x00000000ff760000,0x00000000ff9f0000) ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000) object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000) PSPermGen total 21248K, used 2498K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000) object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c708a0,0x00000000f5ec0000) } Heap PSYoungGen total 34752K, used 1162K [0x00000000fd800000, 0x00000000ffc80000, 0x0000000100000000) eden space 32128K, 2% used [0x00000000fd800000,0x00000000fd8a0ab0,0x00000000ff760000) from space 2624K, 19% used [0x00000000ff9f0000,0x00000000ffa72020,0x00000000ffc80000) to space 2624K, 0% used [0x00000000ff760000,0x00000000ff760000,0x00000000ff9f0000) ParOldGen total 42688K, used 15360K [0x00000000f9c00000, 0x00000000fc5b0000, 0x00000000fd800000) object space 42688K, 35% used [0x00000000f9c00000,0x00000000fab00010,0x00000000fc5b0000) PSPermGen total 21248K, used 2505K [0x00000000f4a00000, 0x00000000f5ec0000, 0x00000000f9c00000) object space 21248K, 11% used [0x00000000f4a00000,0x00000000f4c72638,0x00000000f5ec0000) 加上-XX:+PrintHeapAtGC可以看到每次GC前后堆布局的情况;加上-XX:+PrintTLAB可以看到TLAB的使用状况。 从日志可以看到,这个线程的TLAB才只有321KB,不足以分配楼主例子里的任何一个数组。也就是说例子里的数组全部都不是在TLAB里分配的,而是直接分配在GC堆的共享部分。 我们可以模拟一下楼主的例子的执行过程。整个例子都肯定是在解释器里跑的,所以我们就从解释器来切入。 分配第一个5MB的数组时,解释器要执行newarray字节码指令,来到这里: hotspot/src/share/vm/interpreter/interpreterRuntime.cpp C++代码 [复制代码 收藏代码

  93. IRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread/* thread, BasicType type, jint size))

  94. oop obj = oopFactory::new_typeArray(type, size, CHECK);
  95. thread->set_vm_result(obj);
  96. IRT_END
    IRT_ENTRY(void, InterpreterRuntime::newarray(JavaThread/* thread, BasicType type, jint size)) oop obj = oopFactory::new_typeArray(type, size, CHECK); thread->set_vm_result(obj); IRT_END 里面调用到: hotspot/src/share/vm/memory/oopFactory.cpp C++代码 复制代码 收藏代码

  97. typeArrayOop oopFactory::new_typeArray(BasicType type, int length, TRAPS) {

  98. klassOop type_asKlassOop = Universe::typeArrayKlassObj(type);
  99. typeArrayKlass/* type_asArrayKlass = typeArrayKlass::cast(type_asKlassOop);
  100. typeArrayOop result = type_asArrayKlass->allocate(length, THREAD);
  101. return result;
  102. }
    typeArrayOop oopFactory::new_typeArray(BasicType type, int length, TRAPS) { klassOop type_asKlassOop = Universe::typeArrayKlassObj(type); typeArrayKlass/* type_asArrayKlass = typeArrayKlass::cast(type_asKlassOop); typeArrayOop result = type_asArrayKlass->allocate(length, THREAD); return result; } 进一步调用到: hotspot/src/share/vm/oops/typeArrayKlass.cpp C++代码 复制代码 收藏代码

  103. typeArrayOop typeArrayKlass::allocate(int length, TRAPS) {

  104. assert(log2_element_size() >= 0, "bad scale");
  105. if (length >= 0) {
  106. if (length <= max_length()) {
  107. size_t size = typeArrayOopDesc::object_size(layout_helper(), length);
  108. KlassHandle h_k(THREAD, as_klassOop());
  109. typeArrayOop t;
  110. CollectedHeap/* ch = Universe::heap();
  111. if (size < ch->large_typearray_limit()) {
  112. t = (typeArrayOop)CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL);
  113. } else {
  114. t = (typeArrayOop)CollectedHeap::large_typearray_allocate(h_k, (int)size, length, CHECK_NULL);
  115. }
  116. assert(t->is_parsable(), "Don't publish unless parsable");
  117. return t;
  118. } else {
  119. report_java_out_of_memory("Requested array size exceeds VM limit");
  120. THROW_OOP_0(Universe::out_of_memory_error_array_size());
  121. }
  122. } else {
  123. THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
  124. }
  125. }
    typeArrayOop typeArrayKlass::allocate(int length, TRAPS) { assert(log2_element_size() >= 0, "bad scale"); if (length >= 0) { if (length <= max_length()) { size_t size = typeArrayOopDesc::object_size(layout_helper(), length); KlassHandle h_k(THREAD, as_klassOop()); typeArrayOop t; CollectedHeap/* ch = Universe::heap(); if (size < ch->large_typearray_limit()) { t = (typeArrayOop)CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL); } else { t = (typeArrayOop)CollectedHeap::large_typearray_allocate(h_k, (int)size, length, CHECK_NULL); } assert(t->is_parsable(), "Don't publish unless parsable"); return t; } else { report_java_out_of_memory("Requested array size exceeds VM limit"); THROW_OOP_0(Universe::out_of_memory_error_array_size()); } } else { THROW_0(vmSymbols::java_lang_NegativeArraySizeException()); } } 这个地方我们看到代码根据要分配的数组是否大于large_typearray_limit()做了分支。那么这个界限是多大呢? hotspot/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.hpp C++代码 复制代码 收藏代码

  126. size_t large_typearray_limit() { return FastAllocateSizeLimit; }
    size_t large_typearray_limit() { return FastAllocateSizeLimit; } hotspot/src/share/vm/runtime/globals.hpp C++代码 复制代码 收藏代码

  127. develop(intx, FastAllocateSizeLimit, 128/*K, \

  128. // Note: This value is zero mod 1<<13 for a cheap sparc set. // \
  129. "Inline allocations larger than this in doublewords must go slow")\
    develop(intx, FastAllocateSizeLimit, 128/K, \ // Note: This value is zero mod 1<<13 for a cheap sparc set. /*/ \ "Inline allocations larger than this in doublewords must go slow")\ 这里我们知道了如果对象大小大于128K个doubleword(8字节单元),也就是1MB,就不会进入快速分配路径,而会走慢速分配路径。楼主例子里的数组全部都大于1MB,所以都肯定会走满足分配路径。 不过其实具体到现在HotSpot VM的实现,FastAllocateSizeLimit参数的影响甚微。 它最明显的作用就是让HotSpot Server Compiler(C2)在编译Java方法时看到要分配大于该参数指定的大小的对象时不生成快速分配路径的代码,而直接调用回到VM的慢速分配路径。 而对ParallelScanvege GC来说,这个参数其实设到多少都不影响该GC的行为:本来它就只间接影响到下面讲到的is_noref参数的值,但ParallelScavenge无视了is_noref参数。 回到分配的模拟。接下来调用到: hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp C++代码 复制代码 收藏代码

  130. oop CollectedHeap::large_typearray_allocate(KlassHandle klass,

  131. int size,
  132. int length,
  133. TRAPS) {
  134. debug_only(check_for_valid_allocation_state());
  135. assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
  136. assert(size >= 0, "int won't convert to size_t");
  137. HeapWord/* obj = common_mem_allocate_init(size, true, CHECK_NULL);
  138. post_allocation_setup_array(klass, obj, size, length);
  139. NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
  140. return (oop)obj;
  141. }
    oop CollectedHeap::large_typearray_allocate(KlassHandle klass, int size, int length, TRAPS) { debug_only(check_for_valid_allocation_state()); assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed"); assert(size >= 0, "int won't convert to size_t"); HeapWord/* obj = common_mem_allocate_init(size, true, CHECK_NULL); post_allocation_setup_array(klass, obj, size, length); NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size)); return (oop)obj; } 这里留意它传给common_mem_allocate_init()的is_noref参数为true,就是说不要refill TLAB。 同一文件里, C++代码 复制代码 收藏代码

  142. HeapWord/* CollectedHeap::common_mem_allocate_noinit(size_t size, bool is_noref, TRAPS) {

  143. // Clear unhandled oops for memory allocation. Memory allocation might
  144. // not take out a lock if from tlab, so clear here.
  145. CHECK_UNHANDLED_OOPS_ONLY(THREAD->clear_unhandled_oops();)
  146. if (HAS_PENDING_EXCEPTION) {
  147. NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending"));
  148. return NULL; // caller does a CHECK_0 too
  149. }
  150. // We may want to update this, is_noref objects might not be allocated in TLABs.
  151. HeapWord/* result = NULL;
  152. if (UseTLAB) {
  153. result = CollectedHeap::allocate_from_tlab(THREAD, size);
  154. if (result != NULL) {
  155. assert(!HAS_PENDING_EXCEPTION,
  156. "Unexpected exception, will result in uninitialized storage");
  157. return result;
  158. }
  159. }
  160. bool gc_overhead_limit_was_exceeded = false;
  161. result = Universe::heap()->mem_allocate(size,
  162. is_noref,
  163. false,
  164. &gc_overhead_limit_was_exceeded);
  165. if (result != NULL) {
  166. NOT_PRODUCT(Universe::heap()->
  167. check_for_non_bad_heap_word_value(result, size));
  168. assert(!HAS_PENDING_EXCEPTION,
  169. "Unexpected exception, will result in uninitialized storage");
  170. THREAD->incr_allocated_bytes(size /* HeapWordSize);
  171. return result;
  172. }
  173. if (!gc_overhead_limit_was_exceeded) {
  174. // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
  175. report_java_out_of_memory("Java heap space");
  176. if (JvmtiExport::should_post_resource_exhausted()) {
  177. JvmtiExport::post_resource_exhausted(
  178. JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP,
  179. "Java heap space");
  180. }
  181. THROW_OOP_0(Universe::out_of_memory_error_java_heap());
  182. } else {
  183. // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support
  184. report_java_out_of_memory("GC overhead limit exceeded");
  185. if (JvmtiExport::should_post_resource_exhausted()) {
  186. JvmtiExport::post_resource_exhausted(
  187. JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP,
  188. "GC overhead limit exceeded");
  189. }
  190. THROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit());
  191. }
  192. }
  193. HeapWord/* CollectedHeap::common_mem_allocate_init(size_t size, bool is_noref, TRAPS) {
  194. HeapWord/* obj = common_mem_allocate_noinit(size, is_noref, CHECK_NULL);
  195. init_obj(obj, size);
  196. return obj;
  197. }
    HeapWord/ CollectedHeap::common_mem_allocate_noinit(size_t size, bool is_noref, TRAPS) { // Clear unhandled oops for memory allocation. Memory allocation might // not take out a lock if from tlab, so clear here. CHECK_UNHANDLED_OOPS_ONLY(THREAD->clear_unhandled_oops();) if (HAS_PENDING_EXCEPTION) { NOT_PRODUCT(guarantee(false, "Should not allocate with exception pending")); return NULL; // caller does a CHECK_0 too } // We may want to update this, is_noref objects might not be allocated in TLABs. HeapWord/ result = NULL; if (UseTLAB) { result = CollectedHeap::allocate_from_tlab(THREAD, size); if (result != NULL) { assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); return result; } } bool gc_overhead_limit_was_exceeded = false; result = Universe::heap()->mem_allocate(size, is_noref, false, &gc_overhead_limit_was_exceeded); if (result != NULL) { NOT_PRODUCT(Universe::heap()-> check_for_non_bad_heap_word_value(result, size)); assert(!HAS_PENDING_EXCEPTION, "Unexpected exception, will result in uninitialized storage"); THREAD->incr_allocated_bytes(size / HeapWordSize); return result; } if (!gc_overhead_limit_was_exceeded) { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory("Java heap space"); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, "Java heap space"); } THROW_OOP_0(Universe::out_of_memory_error_java_heap()); } else { // -XX:+HeapDumpOnOutOfMemoryError and -XX:OnOutOfMemoryError support report_java_out_of_memory("GC overhead limit exceeded"); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP, "GC overhead limit exceeded"); } THROW_OOP_0(Universe::out_of_memory_error_gc_overhead_limit()); } } HeapWord/ CollectedHeap::common_mem_allocate_init(size_t size, bool is_noref, TRAPS) { HeapWord/* obj = common_mem_allocate_noinit(size, is_noref, CHECK_NULL); init_obj(obj, size); return obj; } 然后会先调用到: hotspot/src/share/vm/gc_interface/collectedHeap.inline.hpp C++代码 复制代码 收藏代码

  198. HeapWord/ CollectedHeap::allocate_from_tlab(Thread/ thread, size_t size) {

  199. assert(UseTLAB, "should use UseTLAB");
  200. HeapWord/* obj = thread->tlab().allocate(size);
  201. if (obj != NULL) {
  202. return obj;
  203. }
  204. // Otherwise...
  205. return allocate_from_tlab_slow(thread, size);
  206. }
    HeapWord/ CollectedHeap::allocate_from_tlab(Thread/ thread, size_t size) { assert(UseTLAB, "should use UseTLAB"); HeapWord/* obj = thread->tlab().allocate(size); if (obj != NULL) { return obj; } // Otherwise... return allocate_from_tlab_slow(thread, size); } 前面已经说了,TLAB的大小不足以分配楼主例子里的数组,所以thread->tlab().allocate()肯定会返回NULL表示无法分配,于是调用到: hotspot/src/share/vm/gc_interface/collectedHeap.cpp C++代码 复制代码 收藏代码

  207. HeapWord/ CollectedHeap::allocate_from_tlab_slow(Thread/ thread, size_t size) {

  208. // Retain tlab and allocate object in shared space if
  209. // the amount free in the tlab is too large to discard.
  210. if (thread->tlab().free() > thread->tlab().refill_waste_limit()) {
  211. thread->tlab().record_slow_allocation(size);
  212. return NULL;
  213. }
  214. // ...
  215. }
    HeapWord/ CollectedHeap::allocate_from_tlab_slow(Thread/ thread, size_t size) { // Retain tlab and allocate object in shared space if // the amount free in the tlab is too large to discard. if (thread->tlab().free() > thread->tlab().refill_waste_limit()) { thread->tlab().record_slow_allocation(size); return NULL; } // ... } 例子里TLAB几乎是空的,所以这个if判断肯定会通过,TLAB就会记录下一次slow allocation。从GC日志中第一次minor GC的TLAB统计信息可以看到3次slow allocation,其实就是头两个数组(5MB和10MB那两个)成功分配后,第三个数组(又一个5MB的)尝试在TLAB分配失败后打出的日志。 那么回到CollectedHeap::common_mem_allocate_noinit(),它会调用Universe::heap()->mem_allocate(...)进一步尝试分配,传入的is_noref为true,is_tlab为false: hotspot/src/share/vm/gc_implementation/parallelScavengeHeap.cpp C++代码 复制代码 收藏代码

  216. // There are two levels of allocation policy here.

  217. //
  218. // When an allocation request fails, the requesting thread must invoke a VM
  219. // operation, transfer control to the VM thread, and await the results of a
  220. // garbage collection. That is quite expensive, and we should avoid doing it
  221. // multiple times if possible.
  222. //
  223. // To accomplish this, we have a basic allocation policy, and also a
  224. // failed allocation policy.
  225. //
  226. // The basic allocation policy controls how you allocate memory without
  227. // attempting garbage collection. It is okay to grab locks and
  228. // expand the heap, if that can be done without coming to a safepoint.
  229. // It is likely that the basic allocation policy will not be very
  230. // aggressive.
  231. //
  232. // The failed allocation policy is invoked from the VM thread after
  233. // the basic allocation policy is unable to satisfy a mem_allocate
  234. // request. This policy needs to cover the entire range of collection,
  235. // heap expansion, and out-of-memory conditions. It should make every
  236. // attempt to allocate the requested memory.
  237. // Basic allocation policy. Should never be called at a safepoint, or
  238. // from the VM thread.
  239. //
  240. // This method must handle cases where many mem_allocate requests fail
  241. // simultaneously. When that happens, only one VM operation will succeed,
  242. // and the rest will not be executed. For that reason, this method loops
  243. // during failed allocation attempts. If the java heap becomes exhausted,
  244. // we rely on the size_policy object to force a bail out.
  245. HeapWord/* ParallelScavengeHeap::mem_allocate(
  246. size_t size,
  247. bool is_noref,
  248. bool is_tlab,
  249. bool/* gc_overhead_limit_was_exceeded) {
  250. assert(!SafepointSynchronize::is_at_safepoint(), "should not be at safepoint");
  251. assert(Thread::current() != (Thread/*)VMThread::vm_thread(), "should not be in vm thread");
  252. assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock");
  253. // In general gc_overhead_limit_was_exceeded should be false so
  254. // set it so here and reset it to true only if the gc time
  255. // limit is being exceeded as checked below.
  256. /*gc_overhead_limit_was_exceeded = false;
  257. HeapWord/* result = young_gen()->allocate(size, is_tlab);
  258. uint loop_count = 0;
  259. uint gc_count = 0;
  260. while (result == NULL) {
  261. // We don't want to have multiple collections for a single filled generation.
  262. // To prevent this, each thread tracks the total_collections() value, and if
  263. // the count has changed, does not do a new collection.
  264. //
  265. // The collection count must be read only while holding the heap lock. VM
  266. // operations also hold the heap lock during collections. There is a lock
  267. // contention case where thread A blocks waiting on the Heap_lock, while
  268. // thread B is holding it doing a collection. When thread A gets the lock,
  269. // the collection count has already changed. To prevent duplicate collections,
  270. // The policy MUST attempt allocations during the same period it reads the
  271. // total_collections() value!
  272. {
  273. MutexLocker ml(Heap_lock);
  274. gc_count = Universe::heap()->total_collections();
  275. result = young_gen()->allocate(size, is_tlab);
  276. // (1) If the requested object is too large to easily fit in the
  277. // young_gen, or
  278. // (2) If GC is locked out via GCLocker, young gen is full and
  279. // the need for a GC already signalled to GCLocker (done
  280. // at a safepoint),
  281. // ... then, rather than force a safepoint and (a potentially futile)
  282. // collection (attempt) for each allocation, try allocation directly
  283. // in old_gen. For case (2) above, we may in the future allow
  284. // TLAB allocation directly in the old gen.
  285. if (result != NULL) {
  286. return result;
  287. }
  288. if (!is_tlab &&
  289. size >= (young_gen()->eden_space()->capacity_in_words(Thread::current()) / 2)) {
  290. result = old_gen()->allocate(size, is_tlab);
  291. if (result != NULL) {
  292. return result;
  293. }
  294. }
  295. if (GC_locker::is_active_and_needs_gc()) {
  296. // GC is locked out. If this is a TLAB allocation,
  297. // return NULL; the requestor will retry allocation
  298. // of an idividual object at a time.
  299. if (is_tlab) {
  300. return NULL;
  301. }
  302. // If this thread is not in a jni critical section, we stall
  303. // the requestor until the critical section has cleared and
  304. // GC allowed. When the critical section clears, a GC is
  305. // initiated by the last thread exiting the critical section; so
  306. // we retry the allocation sequence from the beginning of the loop,
  307. // rather than causing more, now probably unnecessary, GC attempts.
  308. JavaThread/* jthr = JavaThread::current();
  309. if (!jthr->in_critical()) {
  310. MutexUnlocker mul(Heap_lock);
  311. GC_locker::stall_until_clear();
  312. continue;
  313. } else {
  314. if (CheckJNICalls) {
  315. fatal("Possible deadlock due to allocating while"
  316. " in jni critical section");
  317. }
  318. return NULL;
  319. }
  320. }
  321. }
  322. if (result == NULL) {
  323. // Generate a VM operation
  324. VM_ParallelGCFailedAllocation op(size, is_tlab, gc_count);
  325. VMThread::execute(&op);
  326. // Did the VM operation execute? If so, return the result directly.
  327. // This prevents us from looping until time out on requests that can
  328. // not be satisfied.
  329. if (op.prologue_succeeded()) {
  330. assert(Universe::heap()->is_in_or_null(op.result()),
  331. "result not in heap");
  332. // If GC was locked out during VM operation then retry allocation
  333. // and/or stall as necessary.
  334. if (op.gc_locked()) {
  335. assert(op.result() == NULL, "must be NULL if gc_locked() is true");
  336. continue; // retry and/or stall as necessary
  337. }
  338. // Exit the loop if the gc time limit has been exceeded.
  339. // The allocation must have failed above ("result" guarding
  340. // this path is NULL) and the most recent collection has exceeded the
  341. // gc overhead limit (although enough may have been collected to
  342. // satisfy the allocation). Exit the loop so that an out-of-memory
  343. // will be thrown (return a NULL ignoring the contents of
  344. // op.result()),
  345. // but clear gc_overhead_limit_exceeded so that the next collection
  346. // starts with a clean slate (i.e., forgets about previous overhead
  347. // excesses). Fill op.result() with a filler object so that the
  348. // heap remains parsable.
  349. const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();
  350. const bool softrefs_clear = collector_policy()->all_soft_refs_clear();
  351. assert(!limit_exceeded || softrefs_clear, "Should have been cleared");
  352. if (limit_exceeded && softrefs_clear) {
  353. /*gc_overhead_limit_was_exceeded = true;
  354. size_policy()->set_gc_overhead_limit_exceeded(false);
  355. if (PrintGCDetails && Verbose) {
  356. gclog_or_tty->print_cr("ParallelScavengeHeap::mem_allocate: "
  357. "return NULL because gc_overhead_limit_exceeded is set");
  358. }
  359. if (op.result() != NULL) {
  360. CollectedHeap::fill_with_object(op.result(), size);
  361. }
  362. return NULL;
  363. }
  364. return op.result();
  365. }
  366. }
  367. // The policy object will prevent us from looping forever. If the
  368. // time spent in gc crosses a threshold, we will bail out.
  369. loop_count++;
  370. if ((result == NULL) && (QueuedAllocationWarningCount > 0) &&
  371. (loop_count % QueuedAllocationWarningCount == 0)) {
  372. warning("ParallelScavengeHeap::mem_allocate retries %d times \n\t"
  373. " size=%d %s", loop_count, size, is_tlab ? "(TLAB)" : "");
  374. }
  375. }
  376. return result;
  377. }
    // There are two levels of allocation policy here. // // When an allocation request fails, the requesting thread must invoke a VM // operation, transfer control to the VM thread, and await the results of a // garbage collection. That is quite expensive, and we should avoid doing it // multiple times if possible. // // To accomplish this, we have a basic allocation policy, and also a // failed allocation policy. // // The basic allocation policy controls how you allocate memory without // attempting garbage collection. It is okay to grab locks and // expand the heap, if that can be done without coming to a safepoint. // It is likely that the basic allocation policy will not be very // aggressive. // // The failed allocation policy is invoked from the VM thread after // the basic allocation policy is unable to satisfy a mem_allocate // request. This policy needs to cover the entire range of collection, // heap expansion, and out-of-memory conditions. It should make every // attempt to allocate the requested memory. // Basic allocation policy. Should never be called at a safepoint, or // from the VM thread. // // This method must handle cases where many mem_allocate requests fail // simultaneously. When that happens, only one VM operation will succeed, // and the rest will not be executed. For that reason, this method loops // during failed allocation attempts. If the java heap becomes exhausted, // we rely on the size_policy object to force a bail out. HeapWord/ ParallelScavengeHeap::mem_allocate( size_t size, bool is_noref, bool is_tlab, bool/ gc_overhead_limit_was_exceeded) { assert(!SafepointSynchronize::is_at_safepoint(), "should not be at safepoint"); assert(Thread::current() != (Thread/)VMThread::vm_thread(), "should not be in vm thread"); assert(!Heap_lock->owned_by_self(), "this thread should not own the Heap_lock"); // In general gc_overhead_limit_was_exceeded should be false so // set it so here and reset it to true only if the gc time // limit is being exceeded as checked below. /gc_overhead_limit_was_exceeded = false; HeapWord/ result = young_gen()->allocate(size, is_tlab); uint loop_count = 0; uint gc_count = 0; while (result == NULL) { // We don't want to have multiple collections for a single filled generation. // To prevent this, each thread tracks the total_collections() value, and if // the count has changed, does not do a new collection. // // The collection count must be read only while holding the heap lock. VM // operations also hold the heap lock during collections. There is a lock // contention case where thread A blocks waiting on the Heap_lock, while // thread B is holding it doing a collection. When thread A gets the lock, // the collection count has already changed. To prevent duplicate collections, // The policy MUST attempt allocations during the same period it reads the // total_collections() value! { MutexLocker ml(Heap_lock); gc_count = Universe::heap()->total_collections(); result = young_gen()->allocate(size, is_tlab); // (1) If the requested object is too large to easily fit in the // young_gen, or // (2) If GC is locked out via GCLocker, young gen is full and // the need for a GC already signalled to GCLocker (done // at a safepoint), // ... then, rather than force a safepoint and (a potentially futile) // collection (attempt) for each allocation, try allocation directly // in old_gen. For case (2) above, we may in the future allow // TLAB allocation directly in the old gen. if (result != NULL) { return result; } if (!is_tlab && size >= (young_gen()->eden_space()->capacity_in_words(Thread::current()) / 2)) { result = old_gen()->allocate(size, is_tlab); if (result != NULL) { return result; } } if (GC_locker::is_active_and_needs_gc()) { // GC is locked out. If this is a TLAB allocation, // return NULL; the requestor will retry allocation // of an idividual object at a time. if (is_tlab) { return NULL; } // If this thread is not in a jni critical section, we stall // the requestor until the critical section has cleared and // GC allowed. When the critical section clears, a GC is // initiated by the last thread exiting the critical section; so // we retry the allocation sequence from the beginning of the loop, // rather than causing more, now probably unnecessary, GC attempts. JavaThread/ jthr = JavaThread::current(); if (!jthr->in_critical()) { MutexUnlocker mul(Heap_lock); GC_locker::stall_until_clear(); continue; } else { if (CheckJNICalls) { fatal("Possible deadlock due to allocating while" " in jni critical section"); } return NULL; } } } if (result == NULL) { // Generate a VM operation VM_ParallelGCFailedAllocation op(size, is_tlab, gc_count); VMThread::execute(&op); // Did the VM operation execute? If so, return the result directly. // This prevents us from looping until time out on requests that can // not be satisfied. if (op.prologue_succeeded()) { assert(Universe::heap()->is_in_or_null(op.result()), "result not in heap"); // If GC was locked out during VM operation then retry allocation // and/or stall as necessary. if (op.gc_locked()) { assert(op.result() == NULL, "must be NULL if gc_locked() is true"); continue; // retry and/or stall as necessary } // Exit the loop if the gc time limit has been exceeded. // The allocation must have failed above ("result" guarding // this path is NULL) and the most recent collection has exceeded the // gc overhead limit (although enough may have been collected to // satisfy the allocation). Exit the loop so that an out-of-memory // will be thrown (return a NULL ignoring the contents of // op.result()), // but clear gc_overhead_limit_exceeded so that the next collection // starts with a clean slate (i.e., forgets about previous overhead // excesses). Fill op.result() with a filler object so that the // heap remains parsable. const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded(); const bool softrefs_clear = collector_policy()->all_soft_refs_clear(); assert(!limit_exceeded || softrefs_clear, "Should have been cleared"); if (limit_exceeded && softrefs_clear) { /*gc_overhead_limit_was_exceeded = true; size_policy()->set_gc_overhead_limit_exceeded(false); if (PrintGCDetails && Verbose) { gclog_or_tty->print_cr("ParallelScavengeHeap::mem_allocate: " "return NULL because gc_overhead_limit_exceeded is set"); } if (op.result() != NULL) { CollectedHeap::fill_with_object(op.result(), size); } return NULL; } return op.result(); } } // The policy object will prevent us from looping forever. If the // time spent in gc crosses a threshold, we will bail out. loop_count++; if ((result == NULL) && (QueuedAllocationWarningCount > 0) && (loop_count % QueuedAllocationWarningCount == 0)) { warning("ParallelScavengeHeap::mem_allocate retries %d times \n\t" " size=%d %s", loop_count, size, is_tlab ? "(TLAB)" : ""); } } return result; } 这里会先尝试在young gen里分配 C++代码 复制代码 收藏代码

  378. // Allocation

  379. HeapWord/* allocate(size_t word_size, bool is_tlab) {
  380. HeapWord/* result = eden_space()->cas_allocate(word_size);
  381. return result;
  382. }
    // Allocation HeapWord/ allocate(size_t word_size, bool is_tlab) { HeapWord/ result = eden_space()->cas_allocate(word_size); return result; } 实际上就是尝试在eden里做一次CAS分配。楼主例子里的头两个数组都会在这里分配成功,把eden占用到99%。然后到第三个数组触发GC前试图分配的时候,eden已经没有足够空间了,分配就会失败从而返回NULL。 然后就开始进入“试图分配 -> 不行的话GC -> 再试图分配 -> 不行的话再GC -> 再试图分配”的循环。 做了第一次minor GC后,头两个数组都被GC掉了,eden为空,于是再试图在young gen里分配第三个数组(5MB那个)就成功了。第四个数组(10MB那个)分配的状况与第二个数组类似,都不需要另外触发GC就可以成功。 等到要分配第5个数组(15MB那个)时,堆的状况跟要分配第三个数组时类似,eden都是99%满,所以楼主可能会以为这个数组也会在GC后分配到eden里。实际状况却是它在young gen尝试分配失败后,真的进入GC前还会试图分配,来到ParallelScavengeHeap::mem_allocate()的这个分支: C++代码 复制代码 收藏代码

  383. if (!is_tlab &&

  384. size >= (young_gen()->eden_space()->capacity_in_words(Thread::current()) / 2)) {
  385. result = old_gen()->allocate(size, is_tlab);
  386. if (result != NULL) {
  387. return result;
  388. }
  389. }
    if (!is_tlab && size >= (young_gen()->eden_space()->capacity_in_words(Thread::current()) / 2)) { result = old_gen()->allocate(size, is_tlab); if (result != NULL) { return result; } } 此时eden的capacity有15MB多,一半就是不到8MB,第5个数组有15MB多一点,肯定超过了这个判断条件的限制,于是就直接在old gen尝试分配,成功,就不需要做GC,因而之前的第3和第4个数组就都还在。 等待雨季的到来 写道

1、byte5为什么没有触发minor gc而是直接分配在老生代,byte3和byte4为什么没有被回收? 总结一下过程: 第1个数组: 大小5MB,慢速路径上在young gen分配直接成功; 第2个数组: 大小10MB,慢速路径上在young gen分配直接成功; 第3个数组: 大小5MB,慢速路径上在young gen分配失败, (进入循环)再尝试在young gen分配,还是失败, 没达到去old gen分配的大小限制, 于是真的触发一次minor GC,eden清空, 再尝试在young gen分配,成功; 第4个数组: 大小10MB,慢速路径上在young gen分配直接成功; 第5个数组: 大小15MB,慢速路径上在young gen分配失败, (进入循环)再尝试在young gen分配,还是失败,

达到了去old gen直接分配的大小限制,在old gen上分配成功。没有触发GC。

等待雨季的到来 写道

2、eden区什么时候才会自动扩容到40M,当创建超过20M的对象时,都是直接分配在了老生,而创建大小超过老生代的对象时老生代会自动扩容。 上面的过程已经很清楚了。如果创建20MB的对象,它在eden分配会失败(此时只有15MB多一点的容量),然后又满足了直接在old gen分配的条件(试图分配的大小大于eden的capacity的一半),就跑到old gen分配了。

如果是比较正常的Java程序,创建的多数对象都是小对象,都通过TLAB来分配,那就可以观察到eden经过几次GC后渐渐扩容到最大大小。

等待雨季的到来 写道

其他: 我在用eclipse调试的时候发现,同样的测试代码在执行的时候输出的GC log居然会有很大的变化,有的时候eden区被初始化为8M有的时候初始化为20M。 这个问题我就不太清楚了。通过Eclipse来调试Java程序,在目标Java程序里会产生一些额外的垃圾,我不太肯定这跟您看到的抖动有没有关系。 Spinner 等待雨季的到来的博客 等待雨季的到来 前天

实在是太感谢R大神百忙之中能给出这么细致入微的解释了!受益匪浅!以前我的认识只是eden区会在GC之后分配内存,并不知道如果分配内存大于eden区capacity一半就会直接在old gen分配的规则。 只是由于我对TLAB的具体细节只知甚少,对您下面的eden扩容的解释还是不是太清楚。 引用

如果是比较正常的Java程序,创建的多数对象都是小对象,都通过TLAB来分配,那就可以观察到eden经过几次GC后渐渐扩容到最大大小。 为什么说创建小对象,通过TLAB来分配,eden就会渐渐扩容到最大了呢? 还有一个比较傻的问题,内存区域扩容之后还会再缩小吗?有内存扩容这个设计的原因是不是capacity小得内存区域收集的会更快呢? Spinner 等待雨季的到来的博客 等待雨季的到来 前天

对了,我没有看毕玄的实验,能给个链接吗? 我看的是您推荐的周志明老师写的深入Java虚拟机。 Spinner IcyFenix的博客 IcyFenix 昨天

等待雨季的到来 写道

实在是太感谢R大神百忙之中能给出这么细致入微的解释了!受益匪浅!以前我的认识只是eden区会在GC之后分配内存,并不知道如果分配内存大于eden区capacity一半就会直接在old gen分配的规则。 只是由于我对TLAB的具体细节只知甚少,对您下面的eden扩容的解释还是不是太清楚。 引用

如果是比较正常的Java程序,创建的多数对象都是小对象,都通过TLAB来分配,那就可以观察到eden经过几次GC后渐渐扩容到最大大小。 为什么说创建小对象,通过TLAB来分配,eden就会渐渐扩容到最大了呢? 还有一个比较傻的问题,内存区域扩容之后还会再缩小吗?有内存扩容这个设计的原因是不是capacity小得内存区域收集的会更快呢? 赞R大的详细解答。我被召唤来回答楼上的2个问题“eden是如何扩容的?”,“eden扩容之后还会缩小吗?” 也按照上面一步一步分析代码来解释,但过程有点多,我就只帖方法名,具体代码不粘上来了,代码可以到openjdk的repo上直接看。 1.遇到分配内存指令(InterpreterRuntime中的new或者newarray这些字节码模版) 2.开始分配内存(前面R大写的分配数组oopFactory::new_typeArray或者分配对象instanceKlass::allocate_instance) 3.向CollectedHeap请求内存(入口是CollectedHeap::array_allocate或者CollectedHeap::obj_allocate,两者最终走到CollectedHeap::common_mem_allocate_noinit方法中) 4.如果能够通过TLAB顺利分配(allocate_from_tlab方法)就在TLAB上划 5.否则,通过mem_allocate方法在eden上划。这个是纯虚方法,因此要看CollectedHeap的子类ParallelScavengeHeap(LZ使用PS收集器) 6.在men_allocate方法中,先通过cas_allocate在MutableSpace上划,不成功的话,堕入GC-尝试分配-GC-尝试分配……的循环中,这个R大在前面讲的很详细,其他就不多说了,着重讲GC。 7.进入GC的过程不是直接方法调用,是通过发送VM_ParallelGCFailedAllocation信号给VM线程触发的,最终转到ParallelScavengeHeap::failed_mem_allocate()方法中。 8.该通过PSScavenge::invoke()开始GC过程,实际GC的活是在PSScavenge::invoke_no_policy()做的。 9.invoke_no_policy()方法可以看到,实际上每次GC都会重置eden大小,即调用了ParallelScavengeHeap::resize_young_gen()方法,eden是否会真正改变,取决于这个方法传入的参数,如果和eden的原本大小不一样,那就相当于增加或者缩小了eden了。 10.这里关系有点烦,补一段说清楚一些。resize_young_gen()方法的参数值就是PSAdaptiveSizePolicy::calculated_eden_size_in_bytes()返回值,换句话说,GC是否要改变eden大小,完全是由PSAdaptiveSizePolicy控制的(这个方法在它的父类AdaptiveSizePolicy中,但calculated_eden_size_in_bytes只是简单返回一个数值,数值是在子类中计算的,PSScavenge::invoke_no_policy()中调用了PSAdaptiveSizePolicy::compute_generation_free_space()去改变这个数值)。 11.到这里,eden如何改变就取决于PSAdaptiveSizePolicy::compute_generation_free_space()的计算结果了。PS收集器的特点是可以设置一些目标策略,譬如追求吞吐量,追求最小停顿时间等,这个方法会根据用户选定的目标,调用adjust_for_minor_pause_time()、adjust_eden_for_footprint()、adjust_for_pause_time()等方法去计算eden的值,具体就不一个一个细说了。即使不详细看方法内容,光从eden_increment_with_supplement_aligned_up、eden_decrement_aligned_down这些明显含有“increment”、“decrement”的方法名字,也可以回答“内存区域扩容之后还会再缩小吗?”这个问题了。 PS:如果对这个确实感兴趣,可以加个PrintAdaptiveSizePolicy,在每次重新计算内存区域大小时输出一些信息看看。 PSS:我把自己的理解写完了,但也木有搞清楚PS收集器eden的大小计算,和TLAB分配的联系在哪里,请R大继续讲解。 Spinner

发表回复

>>返回群组首页

高级语言虚拟机群组

相关讨论

© 2003-2012 ITeye.com. [ 京ICP证110151号 京公网安备110105010620 ] 百联优力(北京)投资有限公司 版权所有

perftools查看堆外内存并解决hbase内存溢出

Posted on

perftools查看堆外内存并解决hbase内存溢出

最近线上运行的hbase发现分配了16g内存,但是实际使用了22g,堆外内存达到6g。感觉非常诡异。堆外内存用一般的工具很难查看,可以通过google-perftools来跟踪:

http://code.google.com/p/google-perftools/downloads/list 它的原理是在java应用程序运行时,当调用malloc时换用它的libtcmalloc.so,这样就能做一些统计了

  • 下载http://download.savannah.gnu.org/releases/libunwind/libunwind-0.99-beta.tar.gz,configure;make;sudo make install
  • 下载http://google-perftools.googlecode.com/files/google-perftools-1.8.1.tar.gz, configure --prefix=/home/user/perftools;make;sudo make install
  • 在应用程序启动前加入:export LD_PRELOAD=/home/hadoop/perftools/lib/libtcmalloc.so以及export HEAPPROFILE=/home/user/perftools/test
  • 修改lc_config:sudo vi /etc/ld.so.conf.d/usr_local_lib.conf,加入/usr/local/lib(libunwind的lib所在目录)
  • 执行sudo /sbin/ldconfig,使libunwind生效
  • 启动应用程序,此时会在/home/user/perftools/下看到诸如test_pid.xxxx.heap的heap文件,可使用bin/pprof --text $JAVA_HOME/bin/java test_pid.xxxx.heap来查看 通过perftools查看到以下内容: Java代码 收藏代码
  1. Total: 3263.2 MB
  2. 3145.2 96.4% 96.4% 3145.2 96.4% zcalloc
  3. 83.8 2.6% 99.0% 83.8 2.6% os::malloc
  4. 30.0 0.9% 99.9% 30.0 0.9% init
  5. 2.2 0.1% 99.9% 2.2 0.1% ObjectSynchronizer::omAlloc
  6. 1.0 0.0% 100.0% 3144.1 96.4% Java_java_util_zip_Deflater_init
  7. 0.6 0.0% 100.0% 0.7 0.0% readCEN

Total: 3263.2 MB

3145.2 96.4% 96.4% 3145.2 96.4% zcalloc 83.8 2.6% 99.0% 83.8 2.6% os::malloc

30.0   0.9%  99.9%     30.0   0.9% init
 2.2   0.1%  99.9%      2.2   0.1% ObjectSynchronizer::omAlloc

 1.0   0.0% 100.0%   3144.1  96.4% Java_java_util_zip_Deflater_init
 0.6   0.0% 100.0%      0.7   0.0% readCEN
可见调用了java.util.zip.Deflater占用绝大多数。了解到这个deflater存在无法释放内存的bug,于是编写btrace查看是否进入了这个函数:

Java代码 收藏代码

  1. import static com.sun.btrace.BTraceUtils./*;
  2. import com.sun.btrace.annotations./*;
  3. import java.nio.ByteBuffer;
  4. import java.lang.Thread;
  5. @BTrace public class TestRegion1{
  6. @OnMethod(
  7. clazz="java.util.zip.Deflater",
  8. method="deflate"
  9. )
  10. public static void traceCacheBlock(){
  11. println("deflate?");
  12. }
  13. }

import static com.sun.btrace.BTraceUtils./*;

import com.sun.btrace.annotations./*;

import java.nio.ByteBuffer; import java.lang.Thread;

@BTrace public class TestRegion1{

@OnMethod( clazz="java.util.zip.Deflater",

  method="deflate"

)

public static void traceCacheBlock(){ println("deflate?");

} } 发现果然在不停调用这行代码。应该如何办呢? 由于deflater是gzip需要使用的代码,查看用户创建的表,发现COMPRESSOR设置的是GZ,尝试调整为LZO,结果发现btrace无法进入上述代码,再通过perftools查看时,堆内存不再申请,完全不再申请... 小插曲,perftools的作者是个老实人,提供了zip版下载,但是不提供安装文件,原因?在README中有以下一段话: Html代码 收藏代码

  1. I don't know very much about how to install DLLs on Windows, so you'll
  2. have to figure out that part for yourself.

I don't know very much about how to install DLLs on Windows, so you'll

have to figure out that part for yourself.

Java 7 的新特性一览表

Posted on

Java 7 的新特性一览表

Java 7 的新特性一览表

红薯 发布于: 2011年07月27日 (82评)

分享到

新浪微博腾讯微博 收藏+72

官方说是 7月28日 正式发布 Java 7 ,正常的话我们应该在 7月29日 看到这个版本。很快了,就两天时间。

发布之前让我们先来看看 Java 7 都有什么新特性吧。

Java 7 的架构图:

新特性一览表:

Swing

com.sun.java.swing 包移到

javax.swing 包中,详情: javax.swing.plaf.nimbus

网络

集合

RIA/发布

os attribute in the

information and

resources elements can now contain specific versions of Windows, such as Windows Vista or Windows 7.

  • Applications can use the

install attribute in the

shortcut element to specify their their desire to be installed. Installed applications are not removed when the Java Web Start cache is cleared, but can be explicitly removed using the Java Control Panel.

  • Java Web Start applications can be deployed without specifying the

codebase attribute; see Deploying Without Codebase

XML

java.lang 包

Java 虚拟机

Java I/O

java.nio.file 包以及相关的包 java.nio.file.attribute 提供对文件 I/O 以及访问文件系统的全面支持,请看 File I/O (featuring NIO.2).

  • 目录

/sample/nio/chatserver/ 包含使用 java.nio.file 包的演示程序

  • 目录

/demo/nio/zipfs/ 包含 NIO.2 NFS 文件系统的演示程序

安全性

并发

  • fork/join 框架,基于 ForkJoinPool 类,是 Executor 接口的实现,设计它用来进行高效的运行大量任务;使用 work-stealing 技术用来保证大量的 worker 线程工作,特别适合多处理器环境,详情请看 Fork/Join

  • 目录

/sample/forkjoin/ 包含了 fork/join 框架的演示程序

Java 2D

xrender .

国际化

/demo/jfc/Font2DTest/ 包含 Unicode 6.0 的演示程序

  • Java SE 7 可容纳在 ISO 4217 中新的货币,详情请看 Currency 类.

Java 编程语言特性

JDBC 4.1

  • 支持使用 try -with-resources 语句进行自动的资源释放,包括连接、语句和结果集
  • 支持 RowSet 1.1

Java 的详细介绍:请点这里 Java 的下载地址:请点这里 想通过手机客户端访问开源中国:请点这里 本站文章除注明转载外,均为本站原创或编译 欢迎任何形式的转载,但请务必注明出处,尊重他人劳动共创开源社区 转载请注明:文章转载自:开源中国社区 [http://www.oschina.net] 本文标题:Java 7 的新特性一览表 本文地址:http://www.oschina.net/news/20119/new-features-of-java-7

旧一篇:Kohana 3.2 分支发布 2年前 新一篇:程序员越老越优秀吗? 2年前 相关资讯

X你也许会喜欢

回到顶部发表评论 网友评论,共 82 条

目前不关心

我不想做小白鼠啊~

鼓掌!!!

有几项还是非常值得期待的

期待做小白鼠。。。

不错不错~~ JAVA加油

又要努力学习了

都是些语法糖,用工具类有时候做的可能比这更好

2000年以前Java走在其他语言之前(虚拟执行的概念以及隔离底层变化的机制) 2005年以前Java和追上来的语言一起前行 2005年以后Java...... 2005年以前做过几个Java的项目,包括B/S和桌面应用,从语言机制上说Exception机制很烦人,从类库上说太一般了,尤其是UI库和IO库:So Stupid,从打包机制来说浪费了大量的时间,从配置来说.....

引用来自“heronote”的评论

2000年以前Java走在其他语言之前(虚拟执行的概念以及隔离底层变化的机制) 2005年以前Java和追上来的语言一起前行 2005年以后Java...... 2005年以前做过几个Java的项目,包括B/S和桌面应用,从语言机制上说Exception机制很烦人,从类库上说太一般了,尤其是UI库和IO库:So Stupid,从打包机制来说浪费了大量的时间,从配置来说..... 哥们,貌似你Java很厉害阿,不过看到你最后一段的评论,我还是感觉你太肤浅了,你说的Exception很烦人,我想问你哪种主流编程语言没有这一项?你说UI的设计一般,你也做个跨平台的UI类库,你说I/O库一般,正说明你连Java还不入门,I/O库是Java实现的接近完美的一个类库!打包机制浪费了时间,别跟我说这些没用的,那还不是为了方便程序员阿?

鼓掌!!!

引用来自“Java行者”的评论

引用来自“heronote”的评论

2000年以前Java走在其他语言之前(虚拟执行的概念以及隔离底层变化的机制) 2005年以前Java和追上来的语言一起前行 2005年以后Java...... 2005年以前做过几个Java的项目,包括B/S和桌面应用,从语言机制上说Exception机制很烦人,从类库上说太一般了,尤其是UI库和IO库:So Stupid,从打包机制来说浪费了大量的时间,从配置来说..... 哥们,貌似你Java很厉害阿,不过看到你最后一段的评论,我还是感觉你太肤浅了,你说的Exception很烦人,我想问你哪种主流编程语言没有这一项?你说UI的设计一般,你也做个跨平台的UI类库,你说I/O库一般,正说明你连Java还不入门,I/O库是Java实现的接近完美的一个类库!打包机制浪费了时间,别跟我说这些没用的,那还不是为了方便程序员阿?

这跟选老婆是一样的,各花入各眼。讨论技术还是不要扯到人身攻击上了……

引用来自“Java行者”的评论

引用来自“heronote”的评论

2000年以前Java走在其他语言之前(虚拟执行的概念以及隔离底层变化的机制) 2005年以前Java和追上来的语言一起前行 2005年以后Java...... 2005年以前做过几个Java的项目,包括B/S和桌面应用,从语言机制上说Exception机制很烦人,从类库上说太一般了,尤其是UI库和IO库:So Stupid,从打包机制来说浪费了大量的时间,从配置来说..... 哥们,貌似你Java很厉害阿,不过看到你最后一段的评论,我还是感觉你太肤浅了,你说的Exception很烦人,我想问你哪种主流编程语言没有这一项?你说UI的设计一般,你也做个跨平台的UI类库,你说I/O库一般,正说明你连Java还不入门,I/O库是Java实现的接近完美的一个类库!打包机制浪费了时间,别跟我说这些没用的,那还不是为了方便程序员阿?

人家是指Checked Exception很烦人. Checked Exception目前我所知道,Java是唯一使用的语言.而那些后续更先进的语言,都没加入这个feature. 我也认为这是Java的败笔.

引用来自“Java行者”的评论

引用来自“heronote”的评论

2000年以前Java走在其他语言之前(虚拟执行的概念以及隔离底层变化的机制) 2005年以前Java和追上来的语言一起前行 2005年以后Java...... 2005年以前做过几个Java的项目,包括B/S和桌面应用,从语言机制上说Exception机制很烦人,从类库上说太一般了,尤其是UI库和IO库:So Stupid,从打包机制来说浪费了大量的时间,从配置来说..... 哥们,貌似你Java很厉害阿,不过看到你最后一段的评论,我还是感觉你太肤浅了,你说的Exception很烦人,我想问你哪种主流编程语言没有这一项?你说UI的设计一般,你也做个跨平台的UI类库,你说I/O库一般,正说明你连Java还不入门,I/O库是Java实现的接近完美的一个类库!打包机制浪费了时间,别跟我说这些没用的,那还不是为了方便程序员阿?

你的水平不如被你拍的人. I/O完美么? Windows平台的Java,连NIO都没真正实现, 你敢说完美?

我觉得Java的几个无法忍受的缺陷 不是纯面向对象的 不支持泛型 不支持属性 不支持匿名方法 不支持方法对象传递 不支持yield机制 不支持lambda表达式 不支持类型推断

还有就是上面提到的Checked Exception

为什么Java的lambda表达式迟迟不能实装,估计就是因为Checked Exception的缘故 为什么总是有人说建议学习Python/Ruby,就是相比落后的Java,Python/Ruby的编程思维,生产力能得到极大的解放

引用来自“CheckStyle”的评论

引用来自“Java行者”的评论

引用来自“heronote”的评论

2000年以前Java走在其他语言之前(虚拟执行的概念以及隔离底层变化的机制) 2005年以前Java和追上来的语言一起前行 2005年以后Java...... 2005年以前做过几个Java的项目,包括B/S和桌面应用,从语言机制上说Exception机制很烦人,从类库上说太一般了,尤其是UI库和IO库:So Stupid,从打包机制来说浪费了大量的时间,从配置来说..... 哥们,貌似你Java很厉害阿,不过看到你最后一段的评论,我还是感觉你太肤浅了,你说的Exception很烦人,我想问你哪种主流编程语言没有这一项?你说UI的设计一般,你也做个跨平台的UI类库,你说I/O库一般,正说明你连Java还不入门,I/O库是Java实现的接近完美的一个类库!打包机制浪费了时间,别跟我说这些没用的,那还不是为了方便程序员阿?

你的水平不如被你拍的人. I/O完美么? Windows平台的Java,连NIO都没真正实现, 你敢说完美?

"从JDK 1.4开始,Java的标准库中就包含了NIO,即所谓的“New IO”。其中最重要的功能就是提供了“非阻塞”的IO,当然包括了Socket。NonBlocking的IO就是对select(Unix平台下)以及 WaitForMultipleObjects(Windows平台)的封装,提供了高性能、易伸缩的服务架构。" 你自己看吧,别跟我说没用的,我平时不用windows的。

引用来自“CheckStyle”的评论

引用来自“Java行者”的评论

引用来自“heronote”的评论

2000年以前Java走在其他语言之前(虚拟执行的概念以及隔离底层变化的机制) 2005年以前Java和追上来的语言一起前行 2005年以后Java...... 2005年以前做过几个Java的项目,包括B/S和桌面应用,从语言机制上说Exception机制很烦人,从类库上说太一般了,尤其是UI库和IO库:So Stupid,从打包机制来说浪费了大量的时间,从配置来说..... 哥们,貌似你Java很厉害阿,不过看到你最后一段的评论,我还是感觉你太肤浅了,你说的Exception很烦人,我想问你哪种主流编程语言没有这一项?你说UI的设计一般,你也做个跨平台的UI类库,你说I/O库一般,正说明你连Java还不入门,I/O库是Java实现的接近完美的一个类库!打包机制浪费了时间,别跟我说这些没用的,那还不是为了方便程序员阿?

人家是指Checked Exception很烦人. Checked Exception目前我所知道,Java是唯一使用的语言.而那些后续更先进的语言,都没加入这个feature. 我也认为这是Java的败笔.

我想问一下,你怎么知道他指的是“Checked Exception”,你是他的马甲还是什么?至于你说的那些后续的语言都没有加进什么所谓的feature,那我想问你,Java发明之初,她能设计的那么齐全和周到吗?你可能会问,他为什么部把这个feature去掉,现在没法去了,兼容性是个大问题!还有那我问你,C语言没有运用面向对象的思想,是不是也是C语言的败笔?说了那么多,总而言之,言而总之就是:上面的那位哥们他明显的在评论语言的优劣,而我从没有说过哪种语言设计的非常好,我从没有比较过哪种语言谁好谁差,这种比较本身就是一种无知的表现!

引用来自“zantesu”的评论

我觉得Java的几个无法忍受的缺陷 不是纯面向对象的 不支持泛型 不支持属性 不支持匿名方法 不支持方法对象传递 不支持yield机制 不支持lambda表达式 不支持类型推断

还有就是上面提到的Checked Exception

为什么Java的lambda表达式迟迟不能实装,估计就是因为Checked Exception的缘故 为什么总是有人说建议学习Python/Ruby,就是相比落后的Java,Python/Ruby的编程思维,生产力能得到极大的解放 没用过就别发言...

  • 非会员用户 19楼:yissyo发表于 2011-07-28 15:00 (非会员)

鼓掌围观高手

引用来自“SudyX”的评论

引用来自“zantesu”的评论

我觉得Java的几个无法忍受的缺陷 不是纯面向对象的 不支持泛型 不支持属性 不支持匿名方法 不支持方法对象传递 不支持yield机制 不支持lambda表达式 不支持类型推断

还有就是上面提到的Checked Exception

为什么Java的lambda表达式迟迟不能实装,估计就是因为Checked Exception的缘故 为什么总是有人说建议学习Python/Ruby,就是相比落后的Java,Python/Ruby的编程思维,生产力能得到极大的解放 没用过就别发言...

敢问你来解释解释?还是就只有你用过Java么?

 [](http://www.51idc.com/activit/sale_hulan.html "租服务器,就上51IDC")       

网名: (必填) 邮箱: (必填,不公开) 网址: 文明上网,理性发言

JVM调优总结(四)

Posted on

JVM调优总结(四)-垃圾回收面临的问题

如何区分垃圾

上面说到的“引用计数”法,通过统计控制生成对象和删除对象时的引用数来判断。垃圾回收程序收集计数为0的对象即可。但是这种方法无法解决循环引用。所以,后来实现的垃圾判断算法中,都是从程序运行的根节点出发,遍历整个对象引用,查找存活的对象。那么在这种方式的实现中,**垃圾回收从哪儿开始的呢**?即,从哪儿开始查找哪些对象是正在被当前系统使用的。上面分析的堆和栈的区别,其中栈是真正进行程序执行地方,所以要获取哪些对象正在被使用,则需要从Java栈开始。同时,一个栈是与一个线程对应的,因此,如果有多个线程的话,则必须对这些线程对应的所有的栈进行检查。

同时,除了栈外,还有系统运行时的寄存器等,也是存储程序运行数据的。这样,以栈或寄存器中的引用为起点,我们可以找到堆中的对象,又从这些对象找到对堆中其他对象的引用,这种引用逐步扩展,最终以null引用或者基本类型结束,这样就形成了一颗以Java栈中引用所对应的对象为根节点的一颗对象树,如果栈中有多个引用,则最终会形成多颗对象树。在这些对象树上的对象,都是当前系统运行所需要的对象,不能被垃圾回收。而其他剩余对象,则可以视为无法被引用到的对象,可以被当做垃圾进行回收。

因此,垃圾回收的起点是一些根对象(java栈, 静态变量, 寄存器...)。而最简单的Java栈就是Java程序执行的main函数。这种回收方式,也是上面提到的“标记-清除”的回收方式

如何处理碎片

由于不同Java对象存活时间是不一定的,因此,在程序运行一段时间以后,如果不进行内存整理,就会出现零散的内存碎片。碎片最直接的问题就是会导致无法分配大块的内存空间,以及程序运行效率降低。所以,在上面提到的基本垃圾回收算法中,“复制”方式和“标记-整理”方式,都可以解决碎片的问题。

如何解决同时存在的对象创建和对象回收问题

垃圾回收线程是回收内存的,而程序运行线程则是消耗(或分配)内存的,**一个回收内存,一个分配内存**,从这点看,两者是矛盾的。因此,在现有的垃圾回收方式中,要进行垃圾回收前,一般都需要暂停整个应用(即:暂停内存的分配),然后进行垃圾回收,回收完成后再继续应用。这种实现方式是最直接,而且最有效的解决二者矛盾的方式。

但是这种方式有一个很明显的弊端,就是当堆空间持续增大时,垃圾回收的时间也将会相应的持续增大,对应应用暂停的时间也会相应的增大。一些对相应时间要求很高的应用,比如最大暂停时间要求是几百毫秒,那么当堆空间大于几个G时,就很有可能超过这个限制,在这种情况下,垃圾回收将会成为系统运行的一个瓶颈。为解决这种矛盾,有了并发垃圾回收算法,使用这种算法,垃圾回收线程与程序运行线程同时运行。在这种方式下,解决了暂停的问题,但是因为需要在新生成对象的同时又要回收对象,算法复杂性会大大增加,系统的处理能力也会相应降低,同时,“碎片”问题将会比较难解决。