﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-少年阿宾-随笔分类-JVM</title><link>http://www.blogjava.net/stevenjohn/category/52895.html</link><description>那些青春的岁月</description><language>zh-cn</language><lastBuildDate>Mon, 23 Mar 2015 09:08:30 GMT</lastBuildDate><pubDate>Mon, 23 Mar 2015 09:08:30 GMT</pubDate><ttl>60</ttl><item><title> Java编译器、JVM、解释器</title><link>http://www.blogjava.net/stevenjohn/archive/2015/03/23/423743.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Mon, 23 Mar 2015 07:23:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2015/03/23/423743.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/423743.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2015/03/23/423743.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/423743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/423743.html</trackback:ping><description><![CDATA[<a href="http://java.chinaitlab.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">Java</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;虚拟机(JVM)是可运行</span><a href="http://java.chinaitlab.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">Java</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;代码的假想计算机。只要根据JVM规格描述将解释器移植到特定的计算机上，就能保证经过编译的任何Java代码能够在该系统上运行。本文首先简要介绍从Java文件的编译到最终执行的过程，随后对JVM规格描述作一说明。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><strong style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">一.Java源文件的编译、<a href="http://download.chinaitlab.com/" target="_blank" style="color: #336699; text-decoration: none;">下载</a>&nbsp;、解释和执行</strong>&nbsp;<br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　Java应用程序的开发周期包括编译、</span><a href="http://download.chinaitlab.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">下载</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;、解释和执行几个部分。Java编译程序将Java源程序翻译为JVM可执行代码?字节码。这一编译过程同C/</span><a href="http://c.chinaitlab.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">C++</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;的 编译有些不同。当C编译器编译生成一个对象的代码时，该代码是为在某一特定硬件平台运行而产生的。因此，在编译过程中，编译程序通过查表将所有对符号的引 用转换为特定的内存偏移量，以保证程序运行。Java编译器却不将对变量和方法的引用编译为数值引用，也不确定程序执行过程中的内存布局，而是将这些符号 引用信息保留在字节码中，由解释器在运行过程中创立内存布局，然后再通过查表来确定一个方法所在的地址。这样就有效的保证了Java的可移植性和</span><a href="http://security.chinaitlab.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">安全</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;性。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　 　运行JVM字节码的工作是由解释器来完成的。解释执行过程分三部进行：代码的装入、代码的校验和代码的执行。装入代码的工作由"类装载器"（class loader）完成。类装载器负责装入运行一个程序需要的所有代码，这也包括程序代码中的类所继承的类和被其调用的类。当类装载器装入一个类时，该类被放 在自己的名字空间中。除了通过符号引用自己名字空间以外的类，类之间没有其他办法可以影响其他类。在本台计算机上的所有类都在同一地址空间内，而所有从外 部引进的类，都有一个自己独立的名字空间。这使得本地类通过共享相同的名字空间获得较高的运行效率，同时又保证它们与从外部引进的类不会相互影响。当装入 了运行程序需要的所有类后，解释器便可确定整个可执行程序的内存布局。解释器为符号引用同特定的地址空间建立对应关系及查询表。通过在这一阶段确定代码的 内存布局，Java很好地解决了由超类改变而使子类崩溃的问题，同时也防止了代码对地址的非法访问。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　随后，被装入的代码由字节码校验器进行检查。校验器可发现操作数栈溢出，非法数据类型转化等多种错误。通过校验后，代码便开始执行了。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　Java字节码的执行有两种方式：</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　1.即时编译方式：解释器先将字节码编译成机器码，然后再执行该机器码。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　2.解释执行方式：解释器通过每次解释并执行一小段代码来完成Java字节码程 序的所有操作。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　通常采用的是第二种方法。由于JVM规格描述具有足够的灵活性，这使得将字节码翻译为机器代码的工作</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　具有较高的效率。对于那些对运行速度要求较高的应用程序，解释器可将Java字节码即时编译为机器码，从而很好地保证了Java代码的可移植性和高性能。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><strong style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">二.JVM规格描述</strong>&nbsp;<br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　 　JVM的设计目标是提供一个基于抽象规格描述的计算机模型，为解释程序开发人员提很好的灵活性，同时也确保Java代码可在符合该规范的任何系统上运 行。JVM对其实现的某些方面给出了具体的定义，特别是对Java可执行代码，即字节码(Bytecode)的格式给出了明确的规格。这一规格包括操作码 和操作数的语法和数值、标识符的数值表示方式、以及Java类文件中的Java对象、常量缓冲池在JVM的</span><a href="http://www.storworld.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">存储</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;映象。这些定义为JVM解释器开发人员提供了所需的信息和开发环境。Java的设计者希望给开发人员以随心所欲使用Java的自由。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　JVM定义了控制Java代码解释执行和具体实现的五种规格，它们是：</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　JVM指令系统</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　JVM寄存器</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　JVM栈结构</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　JVM碎片回收堆</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　JVM</span><a href="http://www.storworld.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">存储</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;区</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　2.1JVM指令系统</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　 　JVM指令系统同其他计算机的指令系统极其相似。Java指令也是由 操作码和操作数两部分组成。操作码为8位二进制数，操作数进紧随在操作码的后面，其长度根据需要而不同。操作码用于指定一条指令操作的性质（在这里我们采 用汇编符号的形式进行说明），如iload表示从存储器中装入一个整数，anewarray表示为一个新数组分配空间，iand表示两个整数的" 与"，ret用于流程控制，表示从对某一方法的调用中返回。当长度大于8位时，操作数被分为两个以上字节存放。JVM采用了"big endian"的编码方式来处理这种情况，即高位bits存放在低字节中。这同 Motorola及其他的RISC CPU采用的编码方式是一致的，而与Intel采用的"little endian "的编码方式即低位bits存放在低位字节的方法不同。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　Java指令系统是以Java语言的实现为目的设计的，其中包含了用于调用方法和监视多先程系统的指令。Java的8位操作码的长度使得JVM最多有256种指令，目前已使用了160多种操作码。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　2.2JVM指令系统</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　 　所有的CPU均包含用于保存系统状态和处理器所需信息的寄存器组。如果虚拟机定义较多的寄存器，便可以从中得到更多的信息而不必对栈或内存进行访问，这 有利于提高运行速度。然而，如果虚拟机中的寄存器比实际CPU的寄存器多，在实现虚拟机时就会占用处理器大量的时间来用常规存储器模拟寄存器，这反而会降 低虚拟机的效率。针对这种情况，JVM只设置了4个最为常用的寄存器。它们是：</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　pc程序计数器</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　optop操作数栈顶指针</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　frame当前执行环境指针</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　vars指向当前执行环境中第一个局部变量的指针</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　所有寄存器均为32位。pc用于记录程序的执行。optop,frame和vars用于记录指向Java栈区的指针。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　2.3JVM栈结构</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　作为基于栈结构的计算机，Java栈是JVM存储信息的主要方法。当JVM得到一个Java字节码应用程序后，便为该代码中一个类的每一个方法创建一个栈框架，以保存该方法的状态信息。每个栈框架包括以下三类信息：</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　局部变量</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　执行环境</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　操作数栈</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　局部变量用于存储一个类的方法中所用到的局部变量。vars寄存器指向该变量表中的第一个局部变量。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　 　执行环境用于保存解释器对Java字节码进行解释过程中所需的信息。它们是：上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针。执行环境是一个 执行一个方法的控制中心。例如：如果解释器要执行iadd(整数加法)，首先要从frame寄存器中找到当前执行环境，而后便从执行环境中找到操作数栈， 从栈顶弹出两个整数进行加法运算，最后将结果压入栈顶。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　操作数栈用于存储运算所需操作数及运算的结果。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　2.4JVM碎片回收堆</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　Java类的实例所需的存储空间是在堆上分配的。解释器具体承担为类实例分配空间的工作。解释器在为一个实例分配完存储空间后，便开始记录对该实例所占用的内存区域的使用。一旦对象使用完毕，便将其回收到堆中。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　 　在Java语言中，除了new语句外没有其他方法为一对象申请和释放内存。对内存进行释放和回收的工作是由Java运行系统承担的。这允许Java运行 系统的设计者自己决定碎片回收的方法。在SUN公司开发的Java解释器和Hot Java环境中，碎片回收用后台线程的方式来执行。这不但为运行系统提供了良好的性能，而且使程序设计人员摆脱了自己控制内存使用的风险。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　2.5JVM存储区</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　 　JVM有两类存储区：常量缓冲池和方法区。常量缓冲池用于存储类名称、方法和字段名称以及串常量。方法区则用于存储Java方法的字节码。对于这两种存 储区域具体实现方式在JVM规格中没有明确规定。这使得Java应用程序的存储布局必须在运行过程中确定，依赖于具体平台的实现方式。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　JVM是为Java字节码定义的一种独立于具体平台的规格描述，是Java平台独立性的基础。目前的JVM还存在一些限制和不足，有待于进一步的完善，但无论如何，JVM的思想是成功的。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　对比分析：如果把Java原程序想象成我们的</span><a href="http://c.chinaitlab.com/" target="_blank" style="color: #336699; text-decoration: none; font-family: Arial; line-height: 26px; background-color: #ffffff;">C++</a><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;原 程序，Java原程序编译后生成的字节码就相当于C++原程序编译后的80x86的机器码（二进制程序文件），JVM虚拟机相当于80x86计算机系 统,Java解释器相当于80x86CPU。在80x86CPU上运行的是机器码，在Java解释器上运行的是Java字节码。</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;">　　</span><br style="color: #333333; font-family: Arial; line-height: 26px; background-color: #ffffff;" /><span style="background-color: #ffffff;"><font color="#333333" face="Arial"><span style="line-height: 26px;">　　 Java解释器相当于运行Java字节码的&#8220;CPU&#8221;,但该&#8220;CPU&#8221;不是通过硬件实现的，而是用软件实现的。Java解释器实际上就是特定的平台下的一 个应用程序。只要实现了特定平台下的解释器程序，Java字节码就能通过解释器程序在该平台下运行，这是Java跨平台的根本。当前，并不是在所有的平台 下都有相应Java解释器程序，这也是Java并不能在所有的平台下都能运行的原因，它只能在已实现了Java解释器程序的平台下运行。<br /><br /></span></font><br /></span><div><span style="line-height: 26px;">&nbsp; &nbsp; &nbsp; &nbsp;Java主要靠Java虚拟机（JVM）在目标码级实现平台无关性。JVM是一种抽象机器，它附着在具体操作系统之上，本身具有一套虚机器指令，并有自己的栈、寄存器组等。但JVM通常是在软件上而不是在硬件上实现。（目前，SUN系统公司已经设计实现了Java芯片，主要使用在网络计算机NC上。另外，Java芯片的出现也会使Java更容易嵌入到家用电器中。）JVM是Java平台无关的基础，在JVM上，有一个Java解释器用来解释Java编译器编译后的程序。Java编程人员在编写完软件后，通过Java编译器将Java源程序编译为JVM的字节代码。任何一台机器只要配备了Java解释器，就可以运行这个程序，而不管这种字节码是在何种平台上生成的（过程如图1所示）。另外，Java采用的是基于IEEE标准的数据类型。通过JVM保证数据类型的一致性，也确保了Java的平台无关性。&nbsp;</span></div><div><span style="line-height: 26px;"><br /></span></div><div><span style="line-height: 26px;">简单说，java的解释器只是一个基于虚拟机jvm平台的程序&nbsp;</span></div><img src ="http://www.blogjava.net/stevenjohn/aggbug/423743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2015-03-23 15:23 <a href="http://www.blogjava.net/stevenjohn/archive/2015/03/23/423743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>setAccessible</title><link>http://www.blogjava.net/stevenjohn/archive/2015/03/23/423732.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Sun, 22 Mar 2015 16:01:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2015/03/23/423732.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/423732.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2015/03/23/423732.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/423732.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/423732.html</trackback:ping><description><![CDATA[<span style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.200000762939453px; background-color: #ffffff;">由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的&nbsp;</span><br style="font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.200000762939453px; background-color: #ffffff;" /><img src ="http://www.blogjava.net/stevenjohn/aggbug/423732.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2015-03-23 00:01 <a href="http://www.blogjava.net/stevenjohn/archive/2015/03/23/423732.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA中JVM的重排序详细介绍</title><link>http://www.blogjava.net/stevenjohn/archive/2015/03/22/423718.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Sun, 22 Mar 2015 05:27:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2015/03/22/423718.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/423718.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2015/03/22/423718.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/423718.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/423718.html</trackback:ping><description><![CDATA[<span style="font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25px; text-indent: 30px; background-color: #f7fcff;">&nbsp; &nbsp; &nbsp; &nbsp;重排序通常是编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。重排序分为两类：编译期重排序和运行期重排序，分别对应编译时和运行时环境<br /></span><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">在并发程序中，程序员会特别关注不同进程或线程之间的数据同步，特别是多个线程同时修改同一变量时，必须采取可靠的同步或其它措施保障数据被正确地修改，这里的一条重要原则是：不要假设指令执行的顺序，你无法预知不同线程之间的指令会以何种顺序执行。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">但是在单线程程序中，通常我们容易假设指令是顺序执行的，否则可以想象程序会发生什么可怕的变化。理想的模型是：各种指令执行的顺序是唯一且有序的，这个顺序就是它们被编写在代码中的顺序，与处理器或其它因素无关，这种模型被称作顺序一致性模型，也是基于冯&#183;诺依曼体系的模型。当然，这种假设本身是合理的，在实践中也鲜有异常发生，但事实上，没有哪个现代多处理器架构会采用这种模型，因为它是在是太低效了。而在编译优化和CPU流水线中，几乎都涉及到指令重排序。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;"><strong>编译期重排序</strong></p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">编译期重排序的典型就是通过调整指令顺序，在不改变程序语义的前提下，尽可能减少寄存器的读取、存储次数，充分复用寄存器的存储值。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">假设第一条指令计算一个值赋给变量A并存放在寄存器中，第二条指令与A无关但需要占用寄存器（假设它将占用A所在的那个寄存器），第三条指令使用A的值且与第二条指令无关。那么如果按照顺序一致性模型，A在第一条指令执行过后被放入寄存器，在第二条指令执行时A不再存在，第三条指令执行时A重新被读入寄存器，而这个过程中，A的值没有发生变化。通常编译器都会交换第二和第三条指令的位置，这样第一条指令结束时A存在于寄存器中，接下来可以直接从寄存器中读取A的值，降低了重复读取的开销。<br /><br /><strong>重排序对于流水线的意义</strong></p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">现代CPU几乎都采用流水线机制加快指令的处理速度，一般来说，一条指令需要若干个CPU时钟周期处理，而通过流水线并行执行，可以在同等的时钟周期内执行若干条指令，具体做法简单地说就是把指令分为不同的执行周期，例如读取、寻址、解析、执行等步骤，并放在不同的元件中处理，同时在执行单元EU中，功能单元被分为不同的元件，例如加法元件、乘法元件、加载元件、存储元件等，可以进一步实现不同的计算并行执行。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">流水线架构决定了指令应该被并行执行，而不是在顺序化模型中所认为的那样。重排序有利于充分使用流水线，进而达到超标量的效果。<br /><br /><strong>确保顺序性</strong></p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">尽管指令在执行时并不一定按照我们所编写的顺序执行，但毋庸置疑的是，在单线程环境下，指令执行的最终效果应当与其在顺序执行下的效果一致，否则这种优化便会失去意义。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">通常无论是在编译期还是运行期进行的指令重排序，都会满足上面的原则。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;"><strong>Java存储模型中的重排序</strong></p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">在Java存储模型（Java Memory Model, JMM）中，重排序是十分重要的一节，特别是在并发编程中。JMM通过happens-before法则保证顺序执行语义，如果想要让执行操作B的线程观察到执行操作A的线程的结果，那么A和B就必须满足happens-before原则，否则，JVM可以对它们进行任意排序以提高程序性能。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">volatile关键字可以保证变量的可见性，因为对volatile的操作都在Main Memory中，而Main Memory是被所有线程所共享的，这里的代价就是牺牲了性能，无法利用寄存器或Cache，因为它们都不是全局的，无法保证可见性，可能产生脏读。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">volatile还有一个作用就是局部阻止重排序的发生，对volatile变量的操作指令都不会被重排序，因为如果重排序，又可能产生可见性问题。</p><p style="margin: 0px; padding: 5px 0px; font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25.2000007629395px; background-color: #f7fcff;">在保证可见性方面，锁（包括显式锁、对象锁）以及对原子变量的读写都可以确保变量的可见性。但是实现方式略有不同，例如同步锁保证得到锁时从内存里重新读入数据刷新缓存，释放锁时将数据写回内存以保数据可见，而volatile变量干脆都是读写内存。</p><span style="font-family: Tahoma, Helvetica, Arial, 宋体, sans-serif; line-height: 25px; text-indent: 30px; background-color: #f7fcff;"><br /><br /><br /><br /><br /><br /></span><img src ="http://www.blogjava.net/stevenjohn/aggbug/423718.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2015-03-22 13:27 <a href="http://www.blogjava.net/stevenjohn/archive/2015/03/22/423718.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>重排序、内存可见性、Happens-before 关系</title><link>http://www.blogjava.net/stevenjohn/archive/2015/03/22/423716.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Sun, 22 Mar 2015 04:40:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2015/03/22/423716.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/423716.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2015/03/22/423716.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/423716.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/423716.html</trackback:ping><description><![CDATA[<h2>Java 内存模型</h2><p style="margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; font-size: 1.166em !important; background-color: #ffffff;">由于 ConcurrentHashMap 是建立在 Java 内存模型基础上的，为了更好的理解 ConcurrentHashMap，让我们首先来了解一下 Java 的内存模型。</p><p style="margin: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; font-size: 1.166em !important; background-color: #ffffff;">Java 语言的内存模型由一些规则组成，这些规则确定线程对内存的访问如何排序以及何时可以确保它们对线程是可见的。下面我们将分别介绍 Java 内存模型的重排序，内存可见性和 happens-before 关系。</p><h3>重排序</h3><p style="margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; margin-top: -2px !important; font-size: 1.166em !important; background-color: #ffffff;">内存模型描述了程序的可能行为。具体的编译器实现可以产生任意它喜欢的代码 -- 只要所有执行这些代码产生的结果，能够和内存模型预测的结果保持一致。这为编译器实现者提供了很大的自由，包括操作的重排序。</p><p style="margin: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; font-size: 1.166em !important; background-color: #ffffff;">编译器生成指令的次序，可以不同于源代码所暗示的&#8220;显然&#8221;版本。重排序后的指令，对于优化执行以及成熟的全局寄存器分配算法的使用，都是大有脾益的，它使得程序在计算性能上有了很大的提升。</p><p style="margin: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; font-size: 1.166em !important; background-color: #ffffff;">重排序类型包括：</p><ul style="border: 0px; outline: 0px; font-size: 1.166em; vertical-align: baseline; list-style: none; margin: 0px; padding: 0px 0px 8px; color: #333333; line-height: 1.5em; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 5px 0px 5px 11px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline; height: 22px; clear: left; background: url(http://1.www.s81c.com/i/v17/bullets/ibm_lg_bullet_grey_on.gif) 0px 13px no-repeat;">编译器生成指令的次序，可以不同于源代码所暗示的&#8220;显然&#8221;版本。</li><li style="margin: 0px; padding: 5px 0px 5px 11px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline; height: 22px; clear: left; background: url(http://1.www.s81c.com/i/v17/bullets/ibm_lg_bullet_grey_on.gif) 0px 13px no-repeat;">处理器可以乱序或者并行的执行指令。</li><li style="margin: 0px; padding: 5px 0px 5px 11px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline; height: 22px; clear: left; background: url(http://1.www.s81c.com/i/v17/bullets/ibm_lg_bullet_grey_on.gif) 0px 13px no-repeat;">缓存会改变写入提交到主内存的变量的次序。</li></ul><h3>内存可见性</h3><p style="margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; margin-top: -2px !important; font-size: 1.166em !important; background-color: #ffffff;">由于现代可共享内存的多处理器架构可能导致一个线程无法马上（甚至永远）看到另一个线程操作产生的结果。所以 Java 内存模型规定了 JVM 的一种最小保证：什么时候写入一个变量对其他线程可见。</p><p style="margin: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; font-size: 1.166em !important; background-color: #ffffff;">在现代可共享内存的多处理器体系结构中每个处理器都有自己的缓存，并周期性的与主内存协调一致。假设线程 A 写入一个变量值 V，随后另一个线程 B 读取变量 V 的值，在下列情况下，线程 B 读取的值可能不是线程 A 写入的最新值：</p><ul style="border: 0px; outline: 0px; font-size: 1.166em; vertical-align: baseline; list-style: none; margin: 0px; padding: 0px 0px 8px; color: #333333; line-height: 1.5em; font-family: Arial, sans-serif; background-color: #ffffff;"><li style="margin: 0px; padding: 5px 0px 5px 11px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline; height: 22px; clear: left; background: url(http://1.www.s81c.com/i/v17/bullets/ibm_lg_bullet_grey_on.gif) 0px 13px no-repeat;">执行线程 A 的处理器把变量 V 缓存到寄存器中。</li><li style="margin: 0px; padding: 5px 0px 5px 11px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline; height: 22px; clear: left; background: url(http://1.www.s81c.com/i/v17/bullets/ibm_lg_bullet_grey_on.gif) 0px 13px no-repeat;">执行线程 A 的处理器把变量 V 缓存到自己的缓存中，但还没有同步刷新到主内存中去。</li><li style="margin: 0px; padding: 5px 0px 5px 11px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline; height: 22px; clear: left; background: url(http://1.www.s81c.com/i/v17/bullets/ibm_lg_bullet_grey_on.gif) 0px 13px no-repeat;">执行线程 B 的处理器的缓存中有变量 V 的旧值。</li></ul><h3>Happens-before 关系</h3><p style="margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; margin-top: -2px !important; font-size: 1.166em !important; background-color: #ffffff;">happens-before 关系保证：如果线程 A 与线程 B 满足 happens-before 关系，则线程 A 执行动作的结果对于线程 B 是可见的。如果两个操作未按 happens-before 排序，JVM 将可以对他们任意重排序。</p><p style="margin: 0px; padding: 6px 0px; border: 0px; outline: 0px; vertical-align: baseline; font-family: Arial, sans-serif; color: #222222; line-height: 1.5em; font-size: 1.166em !important; background-color: #ffffff;">下面介绍几个与理解 ConcurrentHashMap 有关的 happens-before 关系法则：</p><ol type="1" style="border: 0px; outline: 0px; font-size: 1.166em; vertical-align: baseline; margin: 0px; padding: 0px 0px 0px 20px; color: #333333; line-height: 1.5em; font-family: Arial, sans-serif; list-style-position: initial !important; list-style-image: initial !important; background-color: #ffffff;"><li style="margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline;">程序次序法则：如果在程序中，所有动作 A 出现在动作 B 之前，则线程中的每动作 A 都 happens-before 于该线程中的每一个动作 B。</li><li style="margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline;">监视器锁法则：对一个监视器的解锁 happens-before 于每个后续对同一监视器的加锁。</li><li style="margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline;">Volatile 变量法则：对 Volatile 域的写入操作 happens-before 于每个后续对同一 Volatile 的读操作。</li><li style="margin: 0px; padding: 0px; border: 0px; outline: 0px; font-size: inherit; vertical-align: baseline;">传递性：如果 A happens-before 于 B，且 B happens-before C，则 A happens-before C。</li><li></li></ol><img src ="http://www.blogjava.net/stevenjohn/aggbug/423716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2015-03-22 12:40 <a href="http://www.blogjava.net/stevenjohn/archive/2015/03/22/423716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>探秘Java 7新增垃圾回收器G1特性</title><link>http://www.blogjava.net/stevenjohn/archive/2015/03/09/423325.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Mon, 09 Mar 2015 12:15:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2015/03/09/423325.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/423325.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2015/03/09/423325.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/423325.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/423325.html</trackback:ping><description><![CDATA[<div>【51CTO精选译文】G1垃圾回收器（简称G1 GC）是JDK 7中Java HotSpot VM新引入的垃圾回收器，Java SE 6 Update 14中已经包含了一个G1的体验版本（据51CTO之前的报导，在Java SE 6 u14于6月初登场时，原本Sun的声明是：G1垃圾回收器需要收费方能使用。然而之后不久，Sun表示这是一个误会，修改了原本的发布声明，并表示现在以及将来对G1的使用都是完全免费的），G1是设计用于替代HotSpot低延迟的并行标记/清除垃圾回收器（也叫做CMS）的。</div><div></div><div>Java 7 G1属性</div><div></div><div>G1是一个服务端垃圾回收器，有以下属性：</div><div></div><div>&#9670;并行和并发性：G1利用了当今硬件中存在的并行性，当Java应用程序的线程被停止时，它使用所有可用的CPU（核心，硬件线程等）加速其停止，在停止过程中运行Java线程最小化整个堆栈。</div><div></div><div>&#9670;代：和其他HotSpot GC一样，G1是一代，意味着它在处理新分配的对象（年轻代）和已经生存了一段时间的对象（年老代）时会不同，它主要集中于新对象上的垃圾回收活动，因为它们是最可能回收的，旧对象只是偶尔访问一下，对于大多数Java应用程序，代的垃圾回收对于替代方案具有重要优势。</div><div></div><div>&#9670;压缩：和CMS不同，G1会随时间推移对堆栈进行压缩，压缩消除了潜在的碎片问题，确保长时间运行的操作流畅和一致。</div><div></div><div>&#9670;可预测性：G1比CMS预测性更佳，这都是由于消除了碎片问题带来的好处，再也没有CMS中停止期间出现的负面影响，另外，G1有一个暂停预测模型，允许它满足（或很少超过）暂停时间目标。</div><div></div><div>Java 7 G1描述</div><div></div><div>和其它HotSpot GC相比，G1采用了一个非常不同的堆栈布局方法，在G1中，年轻代和年老代之间没有物理隔离，相反，它们之间有一个连续的堆栈，被分成大小一样的区域（region），年轻代可能是一套非连续的区域，年老代也一样，这就允许G1在年轻代和年老代之间灵活地移动资源。</div><div></div><div>G1中的回收是通过消除暂停发生的，在此期间，幸存者指的是回收集被转移到另一个区域，以便回收区域可以再生，消除暂停是并行的，所有可用的CPU都会参加，大多数消除暂停收集可用的年轻区域，和其它HotSpot GC中的年轻回收是一样的，在暂停期间偶尔也会选择年老区域回收，因为G1在年轻一代回收上还肩负了年老代的回收活动。</div><div></div><div>和CMS相同的是，G1会定期执行一个并发标记暂停，这个阶段的主要职责是识别哪一个年老区域的垃圾对象是最完整的，因为这些是最有效和最值得回收的，和CMS不同的是，G1不会执行并发清除暂停，相反，最有用的年老区域是通过并发标记暂停标识的，在随后的消除暂停期间进行回收。</div><div></div><div>使用G1</div><div></div><div>G1仍然被看做是试验品，可以使用下面两个参数开启它：</div><div></div><div>-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC&nbsp;</div><div>为了设置一个GC暂停时间目标，使用下面的参数：</div><div></div><div>-XX:MaxGCPauseMillis =50 &nbsp;(暂停时间目标50ms)&nbsp;</div><div>使用G1时还可以指定时间间隔，当GC暂停持续时间没有上面给出的时间长时可以这么用：</div><div></div><div>-XX:GCPauseIntervalMillis =200 &nbsp;(暂停间隔目标200ms)&nbsp;</div><div>注意上面两个选项表示的目标，没有承诺和保证，在某些情况下它们可能能够工作，GC不是总是能够执行它们。</div><div></div><div>另外，年轻代的大小可以明确指定影响消除暂停时间：</div><div></div><div>-XX:+G1YoungGenSize=512m (年轻代大小512M)&nbsp;</div><div>G1也使用幸存空间（可能是非连续的区域），它们的大小可以使用一个常见的参数指定，如：</div><div></div><div>-XX:SurvivorRatio=6&nbsp;</div><div>最后，为了运行G1充分发挥其潜力，尝试设置以下两个默认被禁用了的参数，因为它们可能会暴露一个罕见的竞争状态：</div><div></div><div>-XX:+G1ParallelRSetUpdatingEnabled &nbsp;</div><div>&nbsp;</div><div>-XX:+G1ParallelRSetScanningEnabled &nbsp;</div><div>注意当设置了-XX:+PrintGCDetails后，G1比起其它HotSpot GC要啰嗦得多，因为它会打印每个GC线程的计时和其它有助于进行故障排除的信息，如果你想使GC日志更简单，请使用-verbosegc参数。</div><div></div><div>Java 7 G1最新进展</div><div></div><div>G1开发现在主要集中在遗留的可靠性问题和改善性能，同时也在逐步移除下面的限制：</div><div></div><div>&#9670;G1不能完全支持JVM工具接口（JVM TI）或Java管理扩展（JMX），因此关于G1的监视和管理工具很可能不能正常工作；</div><div></div><div>&#9670;G1不支持增量永久性代回收，如果一个应用程序产生了许多类转储，需要永久性代回收，这在完整GC期间是可以实现的；</div><div></div><div>&#9670;从GC暂停时间来说，G1有时表现比CMS好有时比CMS差。</div><div></div><div>原文：Java HotSpot Garbage Collection</div><img src ="http://www.blogjava.net/stevenjohn/aggbug/423325.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2015-03-09 20:15 <a href="http://www.blogjava.net/stevenjohn/archive/2015/03/09/423325.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jvm垃圾回收</title><link>http://www.blogjava.net/stevenjohn/archive/2014/12/24/421801.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Wed, 24 Dec 2014 15:41:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2014/12/24/421801.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/421801.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2014/12/24/421801.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/421801.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/421801.html</trackback:ping><description><![CDATA[<span class="Apple-style-span" style="color: #666666; font-family: Helmet, Freesans, sans-serif; line-height: 23px; background-color: #f7f7f7; ">&nbsp; &nbsp; 在jvm中堆空间划分为三个代：年轻代（Young Generation）、年老代（Old Generation）和永久代（Permanent Generation）。年轻代和年老代是存储动态产生的对象。永久带主要是存储的是java的类信息，包括解析得到的方法、属性、字段等等。永久带基本不参与垃圾回收。我们这里讨论的垃圾回收主要是针对年轻代和年老代。具体如下图。<br /><br /><br /><br /></span><span class="Apple-style-span" style="color: #666666; font-family: Helmet, Freesans, sans-serif; line-height: 23px; background-color: #f7f7f7; "><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 14px; vertical-align: baseline; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: transparent; background-position: initial initial; background-repeat: initial initial; ">年轻代又分成3个部分，一个eden区和两个相同的survior区。刚开始创建的对象都是放置在eden区的。分成这样3个部分，主要是为了生命周期短的对象尽量留在年轻带。当eden区申请不到空间的时候，进行minorGC，把存活的对象拷贝到survior。年老代主要存放生命周期比较长的对象，比如缓存对象。具体jvm内存回收过程描述如下（可以结合上图）：</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 14px; vertical-align: baseline; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: transparent; background-position: initial initial; background-repeat: initial initial; ">1、对象在Eden区完成内存分配<br />2、当Eden区满了，再创建对象，会因为申请不到空间，触发minorGC，进行young(eden+1survivor)区的垃圾回收<br />3、minorGC时，Eden不能被回收的对象被放入到空的survivor（Eden肯定会被清空），另一个survivor里不能被GC回收的对象也会被放入这个survivor，始终保证一个survivor是空的<br />4、当做第3步的时候，如果发现survivor满了，则这些对象被copy到old区，或者survivor并没有满，但是有些对象已经足够Old，也被放入Old区&nbsp;XX:MaxTenuringThreshold<br />5、当Old区被放满的之后，进行fullGC</p><p style="margin-top: 0px; margin-right: 0px; margin-bottom: 1em; margin-left: 0px; padding-top: 0px; padding-right: 0px; padding-bottom: 0px; padding-left: 0px; border-top-width: 0px; border-right-width: 0px; border-bottom-width: 0px; border-left-width: 0px; border-style: initial; border-color: initial; outline-width: 0px; outline-style: initial; outline-color: initial; font-size: 14px; vertical-align: baseline; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: transparent; background-position: initial initial; background-repeat: initial initial; ">在知道垃圾回收机制以后，大家可以在对jvm中堆的各个参数进行优化设置，来提高性能。</p></span><span class="Apple-style-span" style="color: #666666; font-family: Helmet, Freesans, sans-serif; line-height: 23px; background-color: #f7f7f7; "><br /><br /><br /><br /><br /><br /><br /><br /></span><img src ="http://www.blogjava.net/stevenjohn/aggbug/421801.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2014-12-24 23:41 <a href="http://www.blogjava.net/stevenjohn/archive/2014/12/24/421801.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM中的直接引用和符号引用</title><link>http://www.blogjava.net/stevenjohn/archive/2014/12/16/421482.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Tue, 16 Dec 2014 12:14:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2014/12/16/421482.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/421482.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2014/12/16/421482.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/421482.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/421482.html</trackback:ping><description><![CDATA[<p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">JVM在装载class文件的时候，会有一步是将符号引用解析为直接引用的过程。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">那么这里的<span style="color: #cc0000;">直接引用到底是什么呢？</span></span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">对于指向&#8220;类型&#8221;【Class对象】、类变量、类方法的直接引用可能是<span style="background-color: #ffccff;">指向方法区的本地指针</span>。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';"><span style="background-color: #ccccff;">指向实例变量、实例方法的直接引用都是偏移量</span>。<span style="background-color: #ffccff;">实例变量的直接引用可能是从对象的映像开始算起到这个实例变量位置的偏移量</span>。<span style="background-color: #ffccff;">实例方法的直接引用可能是方法表的偏移量</span>。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">在《深入java虚拟机》书的第197页我们可以看到，子类中方法表的偏移量和父类中的方法表的偏移量是一致的。比如说父类中有一个say()方法的偏移量是7，那么子类中say方法的偏移量也是7。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">书中第199页说，通过&#8220;接口引用&#8221;来调用一个方法，jvm必须<span style="background-color: #ffccff;">搜索对象的类的方法表</span>才能找到一个合适的方法。这是因为<span style="background-color: #ffccff;">实现同一个接口的这些类中，不一定所有的接口中的方法在类方法区中的偏移量都是一样的</span>。他们有可能会不一样。这样的话可能就要搜索方法表才能确认要调用的方法在哪里。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">而通过&#8220;类引用&#8221;来调用一个方法的时候，直接通过偏移量就可以找到要调用的方法的位置了。【因为子类中的方法的偏移量跟父类中的偏移量是一致的】</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei'; background-color: #339999;">所以，通过接口引用调用方法会比类引用慢一些。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">下面介绍下什么是接口引用。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">interface A{void say();}</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">class B implements A{}</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">class C{public static void main(String []s){A a=new B();a.say()}}</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">在上面的第三行代码中，就是用&#8220;接口引用&#8221;来调用方法。</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">--------------------------------------------------------------------</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei'; color: #cc0000;">符号引用：</span></p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';">符号引用是一个字符串，它给出了被引用的内容的名字并且可能会包含一些其他关于这个被引用项的信息&#8212;&#8212;这些信息必须足以唯一的识别一个类、字段、方法。这样，对于其他类的符号引用必须给出类的全名。对于其他类的字段，必须给出类名、字段名以及字段描述符。对于其他类的方法的引用必须给出类名、方法名以及方法的描述符。<br /><br /><br /><br /></span></p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">总结：JVM对于直接引用和符号引用的处理是有区别的，可以看到符号引用时，JVM将使用StringBuilder来完成字符串的&nbsp; 添加，而直接引用时则直接使用String来完成;直接引用永远比符号引用效率更快，但实际应用开发中不可能全用直接引用，要提高效能可以考虑按虚拟机的思维来编写你的程序。</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">1.0 直接引用：</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">public class StringAndStringBuilder{<br />&nbsp;&nbsp; public static void main(String[] args){&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println ("s=" + "asdfa");<br />&nbsp;&nbsp; }<br />}</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">反编译后的：</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">F:\java\res\字节码&gt;javap -c StringAndStringBuilder<br />Compiled from "StringAndStringBuilder.java"<br />public class StringAndStringBuilder extends java.lang.Object{<br />public StringAndStringBuilder();<br />&nbsp; Code:<br />&nbsp;&nbsp; 0:&nbsp;&nbsp; aload_0<br />&nbsp;&nbsp; 1:&nbsp;&nbsp; invokespecial&nbsp;&nbsp; #1; //Method java/lang/Object."&lt;init&gt;":()V<br />&nbsp;&nbsp; 4:&nbsp;&nbsp; return</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">public static void main(java.lang.String[]);<br />&nbsp; Code:<br />&nbsp;&nbsp; 0:&nbsp;&nbsp; ldc&nbsp;&nbsp;&nbsp;&nbsp; #2; //String asdfa<br />&nbsp;&nbsp; 2:&nbsp;&nbsp; astore_1<br />&nbsp;&nbsp; 3:&nbsp;&nbsp; getstatic&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #3; //Field java/lang/System.out:Ljava/io/PrintStream;<br />&nbsp;&nbsp; 6:&nbsp;&nbsp; ldc&nbsp;&nbsp;&nbsp;&nbsp; #4; //String s=asdfa<br />&nbsp;&nbsp; 8:&nbsp;&nbsp; invokevirtual&nbsp;&nbsp; #5; //Method java/io/PrintStream.println:(Ljava/lang/Str<br />ing;)V<br />&nbsp;&nbsp; 11:&nbsp; return</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">}</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">&nbsp;</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">2.0 符号引用：</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">public class StringAndStringBuilder{<br />&nbsp;&nbsp; public static void main(String[] args){&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;String s="asdfa";<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;System.out.println ("s=" + s);<br />&nbsp;&nbsp; }<br />}</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">反编译后的：</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">F:\java\res\字节码&gt;javap -c StringAndStringBuilder<br />Compiled from "StringAndStringBuilder.java"<br />public class StringAndStringBuilder extends java.lang.Object{<br />public StringAndStringBuilder();<br />&nbsp; Code:<br />&nbsp;&nbsp; 0:&nbsp;&nbsp; aload_0<br />&nbsp;&nbsp; 1:&nbsp;&nbsp; invokespecial&nbsp;&nbsp; #1; //Method java/lang/Object."&lt;init&gt;":()V<br />&nbsp;&nbsp; 4:&nbsp;&nbsp; return</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">public static void main(java.lang.String[]);<br />&nbsp; Code:<br />&nbsp;&nbsp; 0:&nbsp;&nbsp; ldc&nbsp;&nbsp;&nbsp;&nbsp; #2; //String asdfa<br />&nbsp;&nbsp; 2:&nbsp;&nbsp; astore_1<br />&nbsp;&nbsp; 3:&nbsp;&nbsp; getstatic&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #3; //Field java/lang/System.out:Ljava/io/PrintStream;<br />&nbsp;&nbsp; 6:&nbsp;&nbsp; new&nbsp;&nbsp;&nbsp;&nbsp; #4; //class java/lang/StringBuilder<br />&nbsp;&nbsp; 9:&nbsp;&nbsp; dup<br />&nbsp;&nbsp; 10:&nbsp; invokespecial&nbsp;&nbsp; #5; //Method java/lang/StringBuilder."&lt;init&gt;":()V<br />&nbsp;&nbsp; 13:&nbsp; ldc&nbsp;&nbsp;&nbsp;&nbsp; #6; //String s=<br />&nbsp;&nbsp; 15:&nbsp; invokevirtual&nbsp;&nbsp; #7; //Method java/lang/StringBuilder.append:(Ljava/lang/<br />String;)Ljava/lang/StringBuilder;<br />&nbsp;&nbsp; 18:&nbsp; aload_1<br />&nbsp;&nbsp; 19:&nbsp; invokevirtual&nbsp;&nbsp; #7; //Method java/lang/StringBuilder.append:(Ljava/lang/<br />String;)Ljava/lang/StringBuilder;<br />&nbsp;&nbsp; 22:&nbsp; invokevirtual&nbsp;&nbsp; #8; //Method java/lang/StringBuilder.toString:()Ljava/la<br />ng/String;<br />&nbsp;&nbsp; 25:&nbsp; invokevirtual&nbsp;&nbsp; #9; //Method java/io/PrintStream.println:(Ljava/lang/Str<br />ing;)V<br />&nbsp;&nbsp; 28:&nbsp; return</p><p style="margin: 0px; padding: 0px; font-family: Helvetica, Tahoma, Arial, sans-serif; line-height: 25.1875px; background-color: #ffffff;">}</p><p style="color: #362e2b; font-family: Arial; line-height: 26px; background-color: #ffffff;"><span style="font-family: 'Microsoft YaHei';"><br /></span></p><img src ="http://www.blogjava.net/stevenjohn/aggbug/421482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2014-12-16 20:14 <a href="http://www.blogjava.net/stevenjohn/archive/2014/12/16/421482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> java.lang.OutOfMemoryError: unable to create new native thread</title><link>http://www.blogjava.net/stevenjohn/archive/2014/12/15/421402.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Mon, 15 Dec 2014 04:08:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2014/12/15/421402.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/421402.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2014/12/15/421402.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/421402.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/421402.html</trackback:ping><description><![CDATA[<div>&nbsp;java.lang.OutOfMemoryError:&nbsp;unable&nbsp;to&nbsp;create&nbsp;new&nbsp;native&nbsp;thread<br />引发此问题的原因有两个：<br /><br />1.线程数超过了操作系统的限制。<br /><br />*&nbsp;使用top命令查看系统资源，如果发现剩余内存很多，而又出现此异常，则基本可以肯定是由于操作系统线程数限制引起的。<br /><br />[root@jack&nbsp;~]#&nbsp;top<br />top&nbsp;-&nbsp;11:36:52&nbsp;up&nbsp;5&nbsp;days,&nbsp;&nbsp;1:34,&nbsp;&nbsp;4&nbsp;users,&nbsp;&nbsp;load&nbsp;average:&nbsp;0.00,&nbsp;0.00,&nbsp;0.07<br />Tasks:&nbsp;131&nbsp;total,&nbsp;&nbsp;&nbsp;1&nbsp;running,&nbsp;130&nbsp;sleeping,&nbsp;&nbsp;&nbsp;0&nbsp;stopped,&nbsp;&nbsp;&nbsp;0&nbsp;zombie<br />Cpu(s):&nbsp;&nbsp;0.2%us,&nbsp;&nbsp;0.2%sy,&nbsp;&nbsp;0.0%ni,&nbsp;99.7%id,&nbsp;&nbsp;0.0%wa,&nbsp;&nbsp;0.0%hi,&nbsp;&nbsp;0.0%si,&nbsp;&nbsp;0.0%st<br />Mem:&nbsp;&nbsp;&nbsp;3821320k&nbsp;total,&nbsp;&nbsp;3122236k&nbsp;used,&nbsp;&nbsp;&nbsp;699084k&nbsp;free,&nbsp;&nbsp;&nbsp;112636k&nbsp;buffers<br />Swap:&nbsp;&nbsp;6062072k&nbsp;total,&nbsp;&nbsp;&nbsp;571760k&nbsp;used,&nbsp;&nbsp;5490312k&nbsp;free,&nbsp;&nbsp;&nbsp;840728k&nbsp;cached<br /><br /><br />*&nbsp;在linux下，可以通过&nbsp;ulimit&nbsp;-a&nbsp;查看系统限制<br /><br />[root@jack&nbsp;~]#&nbsp;ulimit&nbsp;-a<br />core&nbsp;file&nbsp;size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(blocks,&nbsp;-c)&nbsp;0<br />data&nbsp;seg&nbsp;size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(kbytes,&nbsp;-d)&nbsp;unlimited<br />scheduling&nbsp;priority&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-e)&nbsp;0<br />file&nbsp;size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(blocks,&nbsp;-f)&nbsp;unlimited<br />pending&nbsp;signals&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-i)&nbsp;29644<br />max&nbsp;locked&nbsp;memory&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(kbytes,&nbsp;-l)&nbsp;64<br />max&nbsp;memory&nbsp;size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(kbytes,&nbsp;-m)&nbsp;unlimited<br />open&nbsp;files&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-n)&nbsp;1024<br />pipe&nbsp;size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(512&nbsp;bytes,&nbsp;-p)&nbsp;8<br />POSIX&nbsp;message&nbsp;queues&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(bytes,&nbsp;-q)&nbsp;819200<br />real-time&nbsp;priority&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-r)&nbsp;0<br />stack&nbsp;size&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(kbytes,&nbsp;-s)&nbsp;10240<br />cpu&nbsp;time&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(seconds,&nbsp;-t)&nbsp;unlimited<br />max&nbsp;user&nbsp;processes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-u)&nbsp;1024<br />virtual&nbsp;memory&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(kbytes,&nbsp;-v)&nbsp;unlimited<br />file&nbsp;locks&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(-x)&nbsp;unlimited<br /><br />max&nbsp;user&nbsp;process即系统可创建最大线程数。<br /><br />*&nbsp;可以使用&nbsp;ulimit&nbsp;-u&nbsp;4096&nbsp;修改max&nbsp;user&nbsp;processes的值，但是只能在当前终端的这个session里面生效，重新登录后仍然是使用系统默认值。<br /><br />正确的修改方式是修改/etc/security/limits.d/90-nproc.conf文件中的值。<br /><br /><br />[root@jack&nbsp;~]#&nbsp;cat&nbsp;/etc/security/limits.d/90-nproc.conf<br />#&nbsp;Default&nbsp;limit&nbsp;for&nbsp;number&nbsp;of&nbsp;user's&nbsp;processes&nbsp;to&nbsp;prevent<br />#&nbsp;accidental&nbsp;fork&nbsp;bombs.<br />#&nbsp;See&nbsp;rhbz&nbsp;#432903&nbsp;for&nbsp;reasoning.<br /><br />*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;soft&nbsp;&nbsp;&nbsp;&nbsp;nproc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1024<br /><br />2.系统内存不足<br />如果通过top命令确认到是内存不足，则可以通过java启动参数&nbsp;-Xss修改每个线程栈大小。减小此参数，可以提高最大线程数。当然，要保证你的线程使用的内存不会超过这个数。<br /><br />当然，如果不是因为系统级别的问题，那就的优化程序了，检查有没有泄露的内存，有没有业务逻辑存在大量并发等等。<br /></div><img src ="http://www.blogjava.net/stevenjohn/aggbug/421402.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2014-12-15 12:08 <a href="http://www.blogjava.net/stevenjohn/archive/2014/12/15/421402.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么新生代有两个survivor?</title><link>http://www.blogjava.net/stevenjohn/archive/2014/12/09/421217.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Tue, 09 Dec 2014 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2014/12/09/421217.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/421217.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2014/12/09/421217.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/421217.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/421217.html</trackback:ping><description><![CDATA[<p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;">&nbsp;StackOverflow上面给出的解释是：</p><p style="margin: 0px; padding: 0px; font-family: Arial; line-height: 26px; background-color: #ffffff;"></p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">The reason for the HotSpot JVM's two survivor spaces is to reduce the need to deal with fragmentation. New objects are allocated in eden space. All well and good. When that's full, you need a GC, so kill stale objects and move live ones to a survivor space, where they can mature for a while before being promoted to the old generation. Still good so far. The next time we run out of eden space, though, we have a conundrum. The next GC comes along and clears out some space in both eden and our survivor space, but the spaces aren't contiguous. So is it better to</p><ol style="background-color: #ffffff; margin: 0px 0px 1em 30px; padding: 0px; border: 0px; vertical-align: baseline; list-style-position: initial; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;"><li style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; background-color: transparent; word-wrap: break-word;">Try to fit the survivors from eden into the holes in the survivor space that were cleared by the GC?</li><li style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; background-color: transparent; word-wrap: break-word;">Shift all the objects in the survivor space down to eliminate the fragmentation, and&nbsp;<span style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; background-color: transparent;">then</span>&nbsp;move the survivors into it?</li><li style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; background-color: transparent; word-wrap: break-word;"><span style="color: #ff0000;">Just say "screw it, we're moving everything around anyway," and copy all of the survivors from both spaces into a completely separate space--the second survivor space--thus leaving you with a clean eden and survivor space where you can repeat the sequence on the next GC?</span></li></ol><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">Sun's answer to the question is obvious.</p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;"><br /></p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">&nbsp; 对于如何达到&#8220;无碎片&#8221;的目的，理解上可能有些困难，下面我把新生代回收机制详细解释一下：</p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">&nbsp; 注意，<span style="color: #ff0000;">两个survivor是交替使用的，在任意一个时刻，必定有一个survivor为空，一个survivor中存放着对象（连续存放，无碎片）</span>。回收过程如下：</p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">&nbsp; S1、GC，将eden中的live对象放入当前不为空的survivor中，将eden中的非live对象回收。如果survivor满了，下次回收执行S2；如果survivor未满，下次回收仍然以S1的方式回收；</p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">&nbsp; S2、GC，将eden和存放着对象的survivor中的live对象放入当前为空的survivor中，将非live对象回收。</p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">&nbsp; 可以看到，上述的新生代回收机制保证了一个survivor为空，另一个非空survivor中无碎片。</p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">&nbsp; 在执行一定次数的minor GC后，会通过Full GC将新生代的survivor中的对象移入老年代。</p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;"><br /></p><p style="margin: 0px 0px 1em; padding: 0px; background-color: #ffffff; border: 0px; vertical-align: baseline; clear: both; word-wrap: break-word; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; line-height: 18px;">&nbsp; 对于理解GC的整个机制，推荐<span style="color: #ff0000;">一篇非常好的文章</span>：<a target="_blank" href="http://www.cubrid.org/blog/dev-platform/understanding-java-garbage-collection/" style="color: #ca0000; text-decoration: none;">http://www.cubrid.org/blog/dev-platform/understanding-java-garbage-collection/</a></p><img src ="http://www.blogjava.net/stevenjohn/aggbug/421217.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2014-12-09 14:16 <a href="http://www.blogjava.net/stevenjohn/archive/2014/12/09/421217.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 新生代、年轻代、老年代</title><link>http://www.blogjava.net/stevenjohn/archive/2013/09/07/403790.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Sat, 07 Sep 2013 11:09:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2013/09/07/403790.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/403790.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2013/09/07/403790.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/403790.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/403790.html</trackback:ping><description><![CDATA[<!--StartFragment -->

<div>年青代=新生代（eden space）+2个survivor<br />年青代用来存放新近创建的对象，尺寸随堆大小的增大和减小而相应的变化，默认值是保持为堆大小的1/15，可以通过-Xmn参数设置年青代为固定大小，也可以通过-XX:NewRatio来设置年青代与年老代的大小比例，年青代的特点是对象更新速度快，在短时间内产生大量的&#8220;死亡对象&#8221;。<br />年轻代的特点是产生大量的死亡对象,并且要是产生连续可用的空间,&nbsp;所以使用复制清除算法和并行收集器进行垃圾回收.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对年轻代的垃圾回收称作初级回收&nbsp;(minor&nbsp;gc)<br /><br />初级回收将年轻代分为三个区域,&nbsp;&nbsp;一个新生代&nbsp;,&nbsp;2个大小相同的复活代,&nbsp;&nbsp;应用程序只能使用一个新生代和一个复活代,&nbsp;当发生初级垃圾回收的时候,gc挂起程序,&nbsp;然后将新生代和复活代中的存活对象复制到另外一个非活动的复活代中,然后一次性清除新生代和复活代,将原来的非复活代标记成为活动复活代.&nbsp;&nbsp;&nbsp;&nbsp;将在指定次数回收后仍然存在的对象移动到年老代中,&nbsp;初级回收后,得到一个空的可用的新生代.<br /></div><img src ="http://www.blogjava.net/stevenjohn/aggbug/403790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2013-09-07 19:09 <a href="http://www.blogjava.net/stevenjohn/archive/2013/09/07/403790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM中类加载顺序及classpath简介</title><link>http://www.blogjava.net/stevenjohn/archive/2013/09/07/403787.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Sat, 07 Sep 2013 07:43:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2013/09/07/403787.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/403787.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2013/09/07/403787.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/403787.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/403787.html</trackback:ping><description><![CDATA[<div>昨天，看一个build Standalone中databrusher的一个脚本，发现一个Java类似乎没有在classpath中，好像也可一直运行了。很疑惑，问了对应的开发同学，然后自己好好看了下它的代码，才知道了原理。</div><div>命令是：$JAVA_HOME/bin/java $JAVA_OPTS com.alibaba.standalone.AppStartor com.alibaba.intl.standalone.databrusher.Startor "$main_class" "$signal_file" "$recivers"</div><div>原理是：Java根据classpath找到，com.alibaba.standalone.AppStartor这个class，运行这个class，会启动一个classloader来加载com.alibaba.intl.standalone.databrusher.Startor（在里面会指定到WORLDS-INF目录下加载类），然后com.alibaba.intl.standalone.databrusher.Startor会启动对应的"$main_class".</div><div>然后，花了挺多时间好好看了一下Java的classloader，了解一下其中的原理和看了下代码。下面也简单总结一下吧。</div><div>java虚拟机是由sun.misc.Launcher来初始化的,也就是java(或java.exe)这个程序来做的.虚拟机按以下顺序搜索并装载所有需要的类:</div><div>&nbsp; 1,引导类:组成java平台的类,包含rt.jar和i18n.jar等基础jar包中的类.</div><div>&nbsp; 2,扩展类:使用java扩展机制的类,都是位于扩展目录($JAVA_HOME/jre/lib/ext)中的.jar档案包.</div><div>&nbsp; 3,用户类:开发者定义的类或者没有使用java扩展机制的第三方产品.你必须在命令行中使用-classpath选项或者使用CLASSPATH环境变量来确定这些类的位，或者自己写ClassLoader加载。</div><div></div><div>Java的class loader的大致情况如下图所示：</div><div>http://renyongjie668.blog.163.com/prevPhDownload.do?host=renyongjie668&amp;albumId=197449439&amp;photoId=6568564371</div><div>bootstrap classloader --&gt;extension classloader--&gt;system classloader</div><div>虚拟机一启动，会先做一些初始化的动作。一旦初始化动作完成之后，就会 产生第一个类别加载器，即所谓的Bootstrap Loader，Bootstrap Loader 是由C++ 所撰写而成，这个Bootstrap Loader所做的初始工作中，除了也做一些基本的初始化动作之外，最重要的就是加载定义在sun.misc 命名空间底下的Launcher.java 之中的ExtClassLoader( 因为是inner class ，所以编译之后会变成Launcher$ExtClassLoader.class) ，并设定其Parent 为null，代表其父加载器为Bootstrap Loader 。然后Bootstrap Loader ,再要求加载定义于sun.misc 命名空间底下的Launcher.java 之中的AppClassLoader( 因为是inner class，所以编译之后会变成Launcher$AppClassLoader.class) ，并设定其Parent 为之前产生的ExtClassLoader 实例。</div><div>a. Bootstrap ClassLoader/启动类加载器</div><div>主要负责java_home/jre/lib目录下的核心 api 或 -Xbootclasspath 选项指定的jar包装入工作.</div><div>b. Extension ClassLoader/扩展类加载器</div><div>主要负责java_home/jre/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作</div><div>c. System ClassLoader/系统类加载器</div><div>主要负责java -classpath/-Djava.class.path或$CLASSPATH变量所指的目录下的类与jar包装入工作.(这里需要说明的是，如果$CLASSPATH为空，jdk会默认将被运行的Java类的当前路径作为一个默认的$CLASSPATH，一但设置了$CLASSPATH变量，则会到$CLASSPATH对应的路径下去寻找相应的类，找不到就会报错。这个结论，我已经经过测试，并且看了下类加载器中源代码）</div><div>d. User Custom ClassLoader/用户自定义类加载类(java.lang.ClassLoader的子类)在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性.</div><div></div><div>为了有更多的了解，写了个简单的Java程序对前面三种classloader能加载类的路径及其parent类进行了测试。代码如下：<br /><div>import java.net.URL;</div><div>import java.net.URLClassLoader;</div><div></div><div>&nbsp;/**</div><div>&nbsp;* @className: IClassLoader</div><div>&nbsp;* @description: 测试三种classloader加载类的路径，及其parent</div><div>&nbsp;* @author: 笑遍世界</div><div>&nbsp;* @createTime: 2010-11-17 下午07:33:40</div><div>&nbsp;*/</div><div>public class IClassLoader {</div><div>&nbsp; &nbsp; public static void main(String[] args) {</div><div>// &nbsp; &nbsp; &nbsp; &nbsp;测试bootstrap classloader 的类加载路径</div><div>&nbsp; &nbsp; &nbsp; &nbsp; URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &lt; urls.length; i++) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println(urls[i].toExternalForm());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp;</div><div>// &nbsp; &nbsp; &nbsp; &nbsp;测试extension classloader 的类加载路径，先打印一个路径，再打印出其parent，然后再打印出类加载路径中的所有jar包</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println("-------------------------------------");</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(System.getProperty("java.ext.dirs"));</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ClassLoader extensionClassloader=ClassLoader.getSystemClassLoader().getParent();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println("the parent of extension classloader : "+extensionClassloader.getParent());</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println("extension classloader can use thess jars:");</div><div>&nbsp; &nbsp; &nbsp; &nbsp; URL[] extURLs = ((URLClassLoader)ClassLoader.getSystemClassLoader().getParent()).getURLs();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; for (int i = 0; i &lt; extURLs.length; i++) {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;System.out.println(extURLs[i]);</div><div>&nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp;&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp;</div><div>// &nbsp; &nbsp; &nbsp;测试system classloader 的类加载路径，其实也就时classpath的路径，并打印出它的parent</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println("-------------------------------------");</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(System.getProperty("java.class.path"));</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println(ClassLoader.getSystemResource(""));</div><div>&nbsp; &nbsp; &nbsp; &nbsp; ClassLoader systemClassloader=ClassLoader.getSystemClassLoader();</div><div>&nbsp; &nbsp; &nbsp; &nbsp; System.out.println("the parent of system classloader : "+systemClassloader.getParent());</div><div>&nbsp; &nbsp; }</div><div>}</div><br /><div>本机(linux+jdk1.5)运行结果如下：</div><div>file:/usr/ali/java/jre/lib/rt.jar</div><div>file:/usr/ali/java/jre/lib/i18n.jar</div><div>file:/usr/ali/java/jre/lib/sunrsasign.jar</div><div>file:/usr/ali/java/jre/lib/jsse.jar</div><div>file:/usr/ali/java/jre/lib/jce.jar</div><div>file:/usr/ali/java/jre/lib/charsets.jar</div><div>file:/usr/ali/java/jre/classes</div><div>-------------------------------------</div><div>/usr/ali/java/jre/lib/ext</div><div>the parent of extension classloader : null</div><div>extension classloader can use thess jars:</div><div>file:/usr/ali/java/jre/lib/ext/emma.jar</div><div>file:/usr/ali/java/jre/lib/ext/localedata.jar</div><div>file:/usr/ali/java/jre/lib/ext/dnsns.jar</div><div>file:/usr/ali/java/jre/lib/ext/sunpkcs11.jar</div><div>file:/usr/ali/java/jre/lib/ext/sunjce_provider.jar</div><div>-------------------------------------</div><div>.:/usr/ali/java/lib/dt.jar:/usr/ali/java/lib/tools.jar</div><div>file:/home/master/workspace/2010_11/bin/</div><div>the parent of system classloader : &nbsp;sun.misc.Launcher$ExtClassLoader@1a5ab41</div><div></div><div>//ps:当前路径.即是/home/master/workspace/2010_11/bin/</div><br /><br /><br /><div>关于Java的classloader其原理还是需要好好理解才能清楚的，仅通过一天的了解，记录为上面那么多吧。更多详细信息，可以参考如下资料：</div><div>http://snandy.javaeye.com/blog/307083</div><div>http://haofenglemon.javaeye.com/blog/426382</div><div>http://blog.csdn.net/zdwzzu2006/archive/2008/04/05/2253982.aspx</div><div>http://wuquanyin1011.javaeye.com/blog/703842</div><div>http://hi.baidu.com/yangzhibin_bai/blog/item/78846cce1cb86b0992457ead.html</div><div>http://www.blogjava.net/clraychen/archive/2008/02/20/180868.html</div><div></div><br /><br /><br /></div><img src ="http://www.blogjava.net/stevenjohn/aggbug/403787.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2013-09-07 15:43 <a href="http://www.blogjava.net/stevenjohn/archive/2013/09/07/403787.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM监控工具介绍jstack, jconsole, jinfo, jmap, jdb, jstat</title><link>http://www.blogjava.net/stevenjohn/archive/2013/07/26/401991.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Fri, 26 Jul 2013 02:48:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2013/07/26/401991.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/401991.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2013/07/26/401991.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/401991.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/401991.html</trackback:ping><description><![CDATA[<a href="http://blog.csdn.net/kelly859/article/details/5827365">http://blog.csdn.net/kelly859/article/details/5827365</a><img src ="http://www.blogjava.net/stevenjohn/aggbug/401991.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2013-07-26 10:48 <a href="http://www.blogjava.net/stevenjohn/archive/2013/07/26/401991.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM性能调优 </title><link>http://www.blogjava.net/stevenjohn/archive/2013/05/31/400030.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Fri, 31 May 2013 07:49:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2013/05/31/400030.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/400030.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2013/05/31/400030.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/400030.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/400030.html</trackback:ping><description><![CDATA[<font size="2">最近因项目存在内存泄漏，故进行大规模的JVM性能调优</font> <span style="font-size: small"><font size="2">，</font></span> 现把经验做一记录。 
<p>一、JVM内存模型及垃圾收集算法</p>
<p>&nbsp;1.根据Java虚拟机规范，JVM将内存划分为：</p>
<ul><li>New（年轻代）</li><li>Tenured（年老代）</li><li>永久代（Perm）</li></ul>
<p>&nbsp; 其中New和Tenured属于堆内存，堆内存会从JVM启动参数（-Xmx:3G）指定的内存中分配，Perm不属于堆内存，有虚拟机直接分配，但可以通过<strong>-XX:PermSize -XX:MaxPermSize</strong> 等参数调整其大小。</p>
<ul><li>年轻代（New）：年轻代用来存放JVM刚分配的Java对象</li><li>年老代（Tenured)：年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代</li><li>永久代（Perm）：永久代存放Class、Method元信息，其大小跟项目的规模、类、方法的量有关，一般设置为128M就足够，设置原则是预留30%的空间。</li></ul>
<p>New又分为几个部分：</p>
<ul><li>Eden：Eden用来存放JVM刚分配的对象</li><li>Survivor1</li><li>Survivro2：两个Survivor空间一样大，当Eden中的对象经过垃圾回收没有被回收掉时，会在两个Survivor之间来回Copy，当满足某个条件，比如Copy次数，就会被Copy到Tenured。显然，Survivor只是增加了对象在年轻代中的逗留时间，增加了被垃圾回收的可能性。</li></ul>
<p>&nbsp;2.垃圾回收算法</p>
<p>&nbsp; 垃圾回收算法可以分为三类，都基于标记-清除（复制）算法：</p>
<ul><li>Serial算法（单线程）</li><li>并行算法</li><li>并发算法</li></ul>
<p>&nbsp; JVM会根据机器的硬件配置对每个内存代选择适合的回收算法，比如，如果机器多于1个核，会对年轻代选择并行算法，关于选择细节请参考JVM调优文档。</p>
<p>&nbsp; 稍微解释下的是，并行算法是用多线程进行垃圾回收，回收期间会暂停程序的执行，而并发算法，也是多线程回收，但期间不停止应用执行。所以，并发算法适用于交互性高的一些程序。经过观察，并发算法会减少年轻代的大小，其实就是使用了一个大的年老代，这反过来跟并行算法相比吞吐量相对较低。</p>
<p>&nbsp; 还有一个问题是，垃圾回收动作何时执行？</p>
<ul><li>当年轻代内存满时，会引发一次普通GC，该GC仅回收年轻代。需要强调的时，年轻代满是指Eden代满，Survivor满不会引发GC</li><li>当年老代满时会引发Full GC，Full GC将会同时回收年轻代、年老代</li><li>当永久代满时也会引发Full GC，会导致Class、Method元信息的卸载</li></ul>
<p>&nbsp; 另一个问题是，何时会抛出OutOfMemoryException，并不是内存被耗空的时候才抛出</p>
<ul><li>JVM98%的时间都花费在内存回收</li><li>每次回收的内存小于2%</li></ul>
<p>&nbsp; 满足这两个条件将触发OutOfMemoryException，这将会留给系统一个微小的间隙以做一些Down之前的操作，比如手动打印Heap Dump。</p>
<p>二、内存泄漏及解决方法</p>
<p>&nbsp;1.系统崩溃前的一些现象：</p>
<ol></ol>
<ul><li>每次垃圾回收的时间越来越长，由之前的10ms延长到50ms左右，FullGC的时间也有之前的0.5s延长到4、5s</li><li>FullGC的次数越来越多，最频繁时隔不到1分钟就进行一次FullGC</li><li>年老代的内存越来越大并且每次FullGC后年老代没有内存被释放</li></ul>
<ol></ol>
<p>&nbsp;之后系统会无法响应新的请求，逐渐到达OutOfMemoryError的临界值。</p>
<p>&nbsp;2.生成堆的dump文件</p>
<p>&nbsp;通过JMX的MBean生成当前的Heap信息，大小为一个3G（整个堆的大小）的hprof文件，如果没有启动JMX可以通过Java的jmap命令来生成该文件。</p>
<p>&nbsp;3.分析dump文件</p>
<p>&nbsp;下面要考虑的是如何打开这个3G的堆信息文件，显然一般的Window系统没有这么大的内存，必须借助高配置的Linux。当然我们可以借助X-Window把Linux上的图形导入到Window。我们考虑用下面几种工具打开该文件：</p>
<ol><li>Visual VM</li><li>IBM HeapAnalyzer</li><li>JDK 自带的Hprof工具</li></ol>
<p>&nbsp;使用这些工具时为了确保加载速度，建议设置最大内存为6G。使用后发现，这些工具都无法直观地观察到内存泄漏，Visual VM虽能观察到对象大小，但看不到调用堆栈；HeapAnalyzer虽然能看到调用堆栈，却无法正确打开一个3G的文件。因此，我们又选用了Eclipse专门的静态内存分析工具：Mat。</p>
<p>&nbsp;4.分析内存泄漏</p>
<p>&nbsp;通过Mat我们能清楚地看到，哪些对象被怀疑为内存泄漏，哪些对象占的空间最大及对象的调用关系。针对本案，在ThreadLocal中有很多的JbpmContext实例，经过调查是JBPM的Context没有关闭所致。</p>
<p>&nbsp;另，通过Mat或JMX我们还可以分析线程状态，可以观察到线程被阻塞在哪个对象上，从而判断系统的瓶颈。</p>
<p>&nbsp;5.回归问题</p>
<p>&nbsp;&nbsp; Q：<span style="color: #0000ff">为什么崩溃前垃圾回收的时间越来越长？</span></p>
<p>&nbsp;&nbsp; A:<span style="color: #ff6600">根据内存模型和垃圾回收算法，垃圾回收分两部分：内存标记、清除（复制），标记部分只要内存大小固定时间是不变的，变的是复制部分，因为每次垃圾回收都有一些回收不掉的内存，所以增加了复制量，导致时间延长。所以，垃圾回收的时间也可以作为判断内存泄漏的依据</span></p>
<p>&nbsp;&nbsp; Q：<span style="color: #0000ff">为什么Full GC的次数越来越多？</span></p>
<p>&nbsp;&nbsp; A：<span style="color: #ff6600">因此内存的积累，逐渐耗尽了年老代的内存，导致新对象分配没有更多的空间，从而导致频繁的垃圾回收</span></p>
<p>&nbsp;&nbsp; Q:<span style="color: #0000ff">为什么年老代占用的内存越来越大？</span></p>
<p>&nbsp;&nbsp; A:<span style="color: #ff6600">因为年轻代的内存无法被回收，越来越多地被Copy到年老代</span></p>
<p>三、性能调优</p>
<p>&nbsp;除了上述内存泄漏外，我们还发现CPU长期不足3%，系统吞吐量不够，针对8core&#215;16G、64bit的Linux服务器来说，是严重的资源浪费。</p>
<p>&nbsp;在CPU负载不足的同时，偶尔会有用户反映请求的时间过长，我们意识到必须对程序及JVM进行调优。从以下几个方面进行：</p>
<ul><li>线程池：解决用户响应时间长的问题</li><li>连接池</li><li>JVM启动参数：调整各代的内存比例和垃圾回收算法，提高吞吐量</li><li>程序算法：改进程序逻辑算法提高性能</li></ul>
<p>&nbsp; 1.Java线程池（java.util.concurrent.ThreadPoolExecutor）</p>
<p>&nbsp;&nbsp;&nbsp; 大多数JVM6上的应用采用的线程池都是JDK自带的线程池，之所以把成熟的Java线程池进行罗嗦说明，是因为该线程池的行为与我们想象的有点出入。Java线程池有几个重要的配置参数：</p>
<ul><li>corePoolSize：核心线程数（最新线程数）</li><li>maximumPoolSize：最大线程数，超过这个数量的任务会被拒绝，用户可以通过RejectedExecutionHandler接口自定义处理方式</li><li>keepAliveTime：线程保持活动的时间</li><li>workQueue：工作队列，存放执行的任务</li></ul>
<p>&nbsp;&nbsp;&nbsp; Java线程池需要传入一个Queue参数（workQueue）用来存放执行的任务，而对Queue的不同选择，线程池有完全不同的行为：</p>
<ul><li><code><a href="http://www.cnblogs.com/chen77716/admin/eclipse-javadoc:%E2%98%82=perth/C:%5C/Program%20Files%5C/Java%5C/jdk1.6.0_10%5C/jre%5C/lib%5C/rt.jar%3Cjava.util.concurrent%28ThreadPoolExecutor.class%E2%98%83ThreadPoolExecutor%E2%98%82SynchronousQueue">SynchronousQueue：</a> </code><code>一个无容量的等待队列，一个线程的insert操作必须等待另一线程的remove操作，采用这个Queue线程池将会为每个任务分配一个新线程</code></li><li><code><a href="http://www.cnblogs.com/chen77716/admin/eclipse-javadoc:%E2%98%82=perth/C:%5C/Program%20Files%5C/Java%5C/jdk1.6.0_10%5C/jre%5C/lib%5C/rt.jar%3Cjava.util.concurrent%28ThreadPoolExecutor.class%E2%98%83ThreadPoolExecutor%E2%98%82LinkedBlockingQueue">LinkedBlockingQueue</a> ：</code> <code>无界队列，采用该Queue，线程池将忽略</code> maximumPoolSize参数，仅用corePoolSize的线程处理所有的任务，未处理的任务便在<code>LinkedBlockingQueue中排队</code></li><li><code><a href="http://www.cnblogs.com/chen77716/admin/eclipse-javadoc:%E2%98%82=perth/C:%5C/Program%20Files%5C/Java%5C/jdk1.6.0_10%5C/jre%5C/lib%5C/rt.jar%3Cjava.util.concurrent%28ThreadPoolExecutor.class%E2%98%83ThreadPoolExecutor%E2%98%82ArrayBlockingQueue">ArrayBlockingQueue：</a> 有界队列，在有界队列和</code> maximumPoolSize的作用下，程序将很难被调优：更大的Queue和小的maximumPoolSize将导致CPU的低负载；小的Queue和大的池，Queue就没起动应有的作用。</li></ul>
<p>&nbsp;&nbsp;&nbsp; 其实我们的要求很简单，希望线程池能跟连接池一样，能设置最小线程数、最大线程数，当最小数&lt;任务&lt;最大数时，应该分配新的线程处理；当任务&gt;最大数时，应该等待有空闲线程再处理该任务。</p>
<p>&nbsp;&nbsp;&nbsp; 但线程池的设计思路是，任务应该放到Queue中，当Queue放不下时再考虑用新线程处理，如果Queue满且无法派生新线程，就拒绝该任务。设计导致&#8220;先放等执行&#8221;、&#8220;放不下再执行&#8221;、&#8220;拒绝不等待&#8221;。所以，根据不同的Queue参数，要提高吞吐量不能一味地增大maximumPoolSize。</p>
<p>&nbsp;&nbsp;&nbsp; 当然，要达到我们的目标，必须对线程池进行一定的封装，幸运的是ThreadPoolExecutor中留了足够的自定义接口以帮助我们达到目标。我们封装的方式是：</p>
<ul><li>以SynchronousQueue作为参数，使maximumPoolSize发挥作用，以防止线程被无限制的分配，同时可以通过提高maximumPoolSize来提高系统吞吐量</li><li>自定义一个RejectedExecutionHandler，当线程数超过maximumPoolSize时进行处理，处理方式为隔一段时间检查线程池是否可以执行新Task，如果可以把拒绝的Task重新放入到线程池，检查的时间依赖keepAliveTime的大小。</li></ul>
<p>&nbsp; 2.连接池（org.apache.commons.dbcp.BasicDataSource）</p>
<p>&nbsp;&nbsp;&nbsp; 在使用org.apache.commons.dbcp.BasicDataSource的时候，因为之前采用了默认配置，所以当访问量大时，通过JMX观察到很多Tomcat线程都阻塞在BasicDataSource使用的Apache ObjectPool的锁上，直接原因当时是因为BasicDataSource连接池的最大连接数设置的太小，默认的BasicDataSource配置，仅使用8个最大连接。</p>
<p>&nbsp;&nbsp;&nbsp; 我还观察到一个问题，当较长的时间不访问系统，比如2天，DB上的Mysql会断掉所以的连接，导致连接池中缓存的连接不能用。为了解决这些问题，我们充分研究了BasicDataSource，发现了一些优化的点：</p>
<ul><li>Mysql默认支持100个链接，所以每个连接池的配置要根据集群中的机器数进行，如有2台服务器，可每个设置为60</li><li>initialSize：参数是一直打开的连接数</li><li>minEvictableIdleTimeMillis：该参数设置每个连接的空闲时间，超过这个时间连接将被关闭</li><li>timeBetweenEvictionRunsMillis：后台线程的运行周期，用来检测过期连接</li><li>maxActive：最大能分配的连接数</li><li>maxIdle：最大空闲数，当连接使用完毕后发现连接数大于maxIdle，连接将被直接关闭。只有initialSize &lt; x &lt; maxIdle的连接将被定期检测是否超期。这个参数主要用来在峰值访问时提高吞吐量。</li><li>initialSize是如何保持的？经过研究代码发现，BasicDataSource会关闭所有超期的连接，然后再打开initialSize数量的连接，这个特性与minEvictableIdleTimeMillis、timeBetweenEvictionRunsMillis一起保证了所有超期的initialSize连接都会被重新连接，从而避免了Mysql长时间无动作会断掉连接的问题。</li></ul>
<p>&nbsp; 3.JVM参数</p>
<p>&nbsp;&nbsp;&nbsp; 在JVM启动参数中，可以设置跟内存、垃圾回收相关的一些参数设置，默认情况不做任何设置JVM会工作的很好，但对一些配置很好的Server和具体的应用必须仔细调优才能获得最佳性能。通过设置我们希望达到一些目标：</p>
<ul><li>GC的时间足够的小</li><li>GC的次数足够的少</li><li>发生Full GC的周期足够的长</li></ul>
<p>&nbsp; 前两个目前是相悖的，要想GC时间小必须要一个更小的堆，要保证GC次数足够少，必须保证一个更大的堆，我们只能取其平衡。</p>
<p>&nbsp;&nbsp; （1）针对JVM堆的设置一般，可以通过-Xms -Xmx限定其最小、最大值，为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间，我们通常把最大、最小设置为相同的值<br />&nbsp;&nbsp; （2）年轻代和年老代将根据默认的比例（1：2）分配堆内存，可以通过调整二者之间的比率NewRadio来调整二者之间的大小，也可以针对回收代，比如年轻代，通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样，为了防止年轻代的堆收缩，我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小</p>
<p>&nbsp;&nbsp; （3）年轻代和年老代设置多大才算合理？这个我问题毫无疑问是没有答案的，否则也就不会有调优。我们观察一下二者大小变化有哪些影响</p>
<ul><li>更大的年轻代必然导致更小的年老代，大的年轻代会延长普通GC的周期，但会增加每次GC的时间；小的年老代会导致更频繁的Full GC</li><li>更小的年轻代必然导致更大年老代，小的年轻代会导致普通GC很频繁，但每次的GC时间会更短；大的年老代会减少Full GC的频率</li><li>如何选择应该依赖应用程序对象生命周期的分布情况：如果应用存在大量的临时对象，应该选择更大的年轻代；如果存在相对较多的持久对象，年老代应该适当增大。但很多应用都没有这样明显的特性，在抉择时应该根据以下两点：（A）本着Full GC尽量少的原则，让年老代尽量缓存常用对象，JVM的默认比例1：2也是这个道理 （B）通过观察应用一段时间，看其他在峰值时年老代会占多少内存，在不影响Full GC的前提下，根据实际情况加大年轻代，比如可以把比例控制在1：1。但应该给年老代至少预留1/3的增长空间</li></ul>
<p>&nbsp; （4）在配置较好的机器上（比如多核、大内存），可以为年老代选择并行收集算法： <strong>-XX:+UseParallelOldGC</strong> ，默认为Serial收集</p>
<p>&nbsp; （5）线程堆栈的设置：每个线程默认会开启1M的堆栈，用于存放栈帧、调用参数、局部变量等，对大多数应用而言这个默认值太了，一般256K就足用。理论上，在内存不变的情况下，减少每个线程的堆栈，可以产生更多的线程，但这实际上还受限于操作系统。</p>
<p>&nbsp; （4）可以通过下面的参数打Heap Dump信息</p>
<ul><li>-XX:HeapDumpPath</li><li>-XX:+PrintGCDetails</li><li>-XX:+PrintGCTimeStamps</li><li>-Xloggc:/usr/aaa/dump/heap_trace.txt</li></ul>
<p>&nbsp;&nbsp;&nbsp; 通过下面参数可以控制OutOfMemoryError时打印堆的信息</p>
<ul><li>-XX:+HeapDumpOnOutOfMemoryError</li></ul>
<p>&nbsp;请看一下一个时间的Java参数配置：（服务器：Linux 64Bit，8Core&#215;16G）</p>
<p>&nbsp;<strong>JAVA_OPTS="$JAVA_OPTS -server -Xms3G -Xmx3G -Xss256k -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseParallelOldGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/aaa/dump -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/usr/aaa/dump/heap_trace.txt -XX:NewSize=1G -XX:MaxNewSize=1G"</strong></p>
<p>经过观察该配置非常稳定，每次普通GC的时间在10ms左右，Full GC基本不发生，或隔很长很长的时间才发生一次</p>
<p><span style="color: #0000ff">通过分析dump文件可以发现，每个1小时都会发生一次Full GC，经过多方求证，只要在JVM中开启了JMX服务，JMX将会1小时执行一次Full GC以清除引用，关于这点请参考附件文档。</span></p>
<p>&nbsp;4.程序算法调优：本次不作为重点</p>
<p>参考资料：</p>
<p>http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html</p><img src ="http://www.blogjava.net/stevenjohn/aggbug/400030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2013-05-31 15:49 <a href="http://www.blogjava.net/stevenjohn/archive/2013/05/31/400030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java GC 垃圾收集 </title><link>http://www.blogjava.net/stevenjohn/archive/2013/05/28/399870.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Tue, 28 May 2013 06:31:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2013/05/28/399870.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/399870.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2013/05/28/399870.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/399870.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/399870.html</trackback:ping><description><![CDATA[<div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">Java堆，分配对象实例所在空间，是GC的主要对象。分为</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">&nbsp;<wbr>新生代(Young Generation/New)</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">&nbsp;<wbr>老年代(Tenured Generation/Old)</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">新生代又划分成</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">&nbsp;<wbr>Eden Space</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">&nbsp;<wbr>From Survivor/Survivor&nbsp;0</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">&nbsp;<wbr>To Survivor/Survivor&nbsp;1</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">新生代要如此划分是因为新生代使用的GC算法是复制收集算法。这种算法效率较高，而GC主要是发生在对象经常消亡的新生代，因此新生代适合使用这种复制收集算法。由于有一个假设：在一次新生代的GC(Minor GC)后大部分的对象占用的内存都会被回收，因此留存的放置GC后仍然活的对象的空间就比较小了。这个留存的空间就是Survivor space：From Survivor或To Survivor。这两个Survivor空间是一样大小的。例如，新生代大小是10M(Xmn10M)，那么缺省情况下(-XX:SurvivorRatio=8)，Eden Space 是8M，From和To都是1M。</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;"></div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">在new一个对象时，先在Eden Space上分配，如果Eden Space空间不够就要做一次Minor GC。Minor GC后，要把Eden和From中仍然活着的对象们复制到To空间中去。如果To空间不能容纳Minor GC后活着的某个对象，那么该对象就被promote到老年代空间。从Eden空间被复制到To空间的对象就有了age=1。此age=1的对象如果在下一次的Minor GC后仍然存活，它还会被复制到另一个Survivor空间(如果认为From和To是固定的，就是又从To回到了From空间)，而它的age=2。如此反复，如果age大于某个阈值(-XX:MaxTenuringThreshold=n)，那个该对象就也可以promote到老年代了。</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;"></div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">如果Survivor空间中相同age(例如，age=5)对象的总和大于等于Survivor空间的一半，那么age&gt;=5的对象在下一次Minor GC后就可以直接promote到老年代，而不用等到age增长到阈值。</div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;"></div><div style="color: #323e32; font-family: simsun; background-color: #ebebeb;">在做Minor GC时，只对新生代做回收，不会回收老年代。即使老年代的对象无人索引也将仍然存活，直到下一次Full GC。</div><img src ="http://www.blogjava.net/stevenjohn/aggbug/399870.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2013-05-28 14:31 <a href="http://www.blogjava.net/stevenjohn/archive/2013/05/28/399870.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM参数配置大全</title><link>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391310.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Wed, 14 Nov 2012 05:48:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391310.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/391310.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391310.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/391310.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/391310.html</trackback:ping><description><![CDATA[<div id="cnblogs_post_body">
<p>前阵子遇到几个面试题都是关于对Java内存控制的,因此从网上找到这篇文章,希望自己对Java的内存分配有重新的认识 </p>
<p>/usr/local/jdk/bin/java -Dresin.home=/usr/local/resin -server -Xms1800M -Xmx1800M -Xmn300M -Xss512K -XX:PermSize=300M -XX:MaxPermSize=300M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:GCTimeRatio=19 -Xnoclassgc -XX:+DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:-CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=70 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+PrintClassHistogram -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -Xloggc:log/gc.log </p>
<ol><li><strong>堆大小设置<br /></strong>JVM 中最大堆大小有三方面限制：相关操作系统的数据模型（32-bt还是64-bit）限制；系统的可用虚拟内存限制；系统的可用物理内存限制。32位系统下，一般限制在1.5G~2G；64为操作系统对内存无限制。我在Windows Server 2003 系统，3.5G物理内存，JDK5.0下测试，最大可设置为1478m。<br /><strong>典型设置：</strong> 
<ul><li>java <strong>-Xmx3550m -Xms3550m -Xmn2g</strong> <strong>-Xss128k</strong><br /><strong>-</strong><strong>Xmx3550m</strong>：设置JVM最大可用内存为3550M。<br /><strong>-Xms3550m</strong>：设置JVM促使内存为3550m。此值可以设置与-Xmx相同，以避免每次垃圾回收完成后JVM重新分配内存。<br /><strong>-Xmn2g</strong>：设置年轻代大小为2G。<strong>整个堆大小=年轻代大小 + 年老代大小 + 持久代大小</strong>。持久代一般固定大小为64m，所以增大年轻代后，将会减小年老代大小。此值对系统性能影响较大，Sun官方推荐配置为整个堆的3/8。<br /><strong>-Xss128k</strong>：设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M，以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下，减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的，不能无限生成，经验值在3000~5000左右。 </li><li>java -Xmx3550m -Xms3550m -Xss128k <strong>-XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0</strong><br /><strong>-XX:NewRatio=4</strong>:设置年轻代（包括Eden和两个Survivor区）与年老代的比值（除去持久代）。设置为4，则年轻代与年老代所占比值为1：4，年轻代占整个堆栈的1/5<br /><strong>-XX:SurvivorRatio=4</strong>：设置年轻代中Eden区与Survivor区的大小比值。设置为4，则两个Survivor区与一个Eden区的比值为2:4，一个Survivor区占整个年轻代的1/6<br /><strong>-XX:MaxPermSize=16m</strong>:设置持久代大小为16m。<br /><strong>-XX:MaxTenuringThreshold=0</strong>：设置垃圾最大年龄。<strong>如果设置为0的话，则年轻代对象不经过Survivor区，直接进入年老代</strong>。对于年老代比较多的应用，可以提高效率。<strong>如果将此值设置为一个较大值，则年轻代对象会在Survivor区进行多次复制，这样可以增加对象再年轻代的存活时间</strong>，增加在年轻代即被回收的概论。</li></ul></li><li><strong>回收器选择<br /></strong>JVM给了三种选择：<strong>串行收集器、并行收集器、并发收集器</strong>，但是串行收集器只适用于小数据量的情况，所以这里的选择主要针对并行收集器和并发收集器。默认情况下，JDK5.0以前都是使用串行收集器，如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后，JVM会根据当前<a href="http://java.sun.com/j2se/1.5.0/docs/guide/vm/server-class.html">系统配置</a>进行判断。 
<ol><li><strong>吞吐量优先</strong>的并行收集器<br />如上文所述，并行收集器主要以到达一定的吞吐量为目标，适用于科学技术和后台处理等。<br /><strong>典型配置</strong>： 
<ul><li>java -Xmx3800m -Xms3800m -Xmn2g -Xss128k <strong>-XX:+UseParallelGC -XX:ParallelGCThreads=20</strong><br /><strong>-XX:+UseParallelGC</strong>：选择垃圾收集器为并行收集器。<strong>此配置仅对年轻代有效。即上述配置下，年轻代使用并发收集，而年老代仍旧使用串行收集。<br /></strong><strong>-XX:ParallelGCThreads=20</strong>：配置并行收集器的线程数，即：同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 </li><li>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 <strong>-XX:+UseParallelOldGC<br /></strong><strong>-XX:+UseParallelOldGC</strong>：配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。 </li><li>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC <strong>-XX:MaxGCPauseMillis=100<br /></strong><strong>-XX:MaxGCPauseMillis=100</strong><strong>:</strong>设置每次年轻代垃圾回收的最长时间，如果无法满足此时间，JVM会自动调整年轻代大小，以满足此值。 </li><li>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100<strong>-XX:+UseAdaptiveSizePolicy<br />-XX:+UseAdaptiveSizePolicy</strong>：设置此选项后，并行收集器会自动选择年轻代区大小和相应的Survivor区比例，以达到目标系统规定的最低相应时间或者收集频率等，此值建议使用并行收集器时，一直打开。</li></ul></li></ol></li><li><strong>响应时间优先</strong>的并发收集器<br />如上文所述，并发收集器主要是保证系统的响应时间，减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。<br /><strong>典型配置</strong>： 
<ul><li>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 <strong>-XX:+UseConcMarkSweepGC -XX:+UseParNewGC<br /></strong><strong>-XX:+UseConcMarkSweepGC</strong>：设置年老代为并发收集。测试中配置这个以后，-XX:NewRatio=4的配置失效了，原因不明。所以，此时年轻代大小最好用-Xmn设置。<br /><strong>-XX:+UseParNewGC</strong>:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上，JVM会根据系统配置自行设置，所以无需再设置此值。 </li><li>java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC <strong>-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection</strong><br /><strong>-XX:CMSFullGCsBeforeCompaction</strong>：由于并发收集器不对内存空间进行压缩、整理，所以运行一段时间以后会产生&#8220;碎片&#8221;，使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。<br /><strong>-XX:+UseCMSCompactAtFullCollection</strong>：打开对年老代的压缩。可能会影响性能，但是可以消除碎片</li></ul></li></ol></li><li><strong>辅助信息<br /></strong>JVM提供了大量命令行参数，打印信息，供调试使用。主要有以下一些： 
<ul><li><strong>-XX:+PrintGC<br /></strong>输出形式<strong>：[GC 118250K-&gt;113543K(130112K), 0.0094143 secs]</strong> 
<p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Full GC 121376K-&gt;10414K(130112K), 0.0650971 secs]</strong></p></li><li><strong>-XX:+PrintGCDetails<br /></strong>输出形式<strong>：[GC [DefNew: 8614K-&gt;781K(9088K), 0.0123035 secs] 118250K-&gt;113543K(130112K), 0.0124633 secs]</strong> 
<p><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [GC [DefNew: 8614K-&gt;8614K(9088K), 0.0000665 secs][Tenured: 112761K-&gt;10414K(121024K), 0.0433488 secs] 121376K-&gt;10414K(130112K), 0.0436268 secs]</strong></p></li><li><strong>-XX:+PrintGCTimeStamps</strong> -XX:+PrintGC：PrintGCTimeStamps可与上面两个混合使用<br />输出形式：<strong>11.851: [GC 98328K-&gt;93620K(130112K), 0.0082960 secs]<br /></strong></li><li><strong>-XX:+PrintGCApplicationConcurrentTime:</strong>打印每次垃圾回收前，程序未中断的执行时间。可与上面混合使用<br />输出形式：<strong>Application time: 0.5291524 seconds<br /></strong></li><li><strong>-XX:+PrintGCApplicationStoppedTime</strong>：打印垃圾回收期间程序暂停的时间。可与上面混合使用<br />输出形式：<strong>Total time for which application threads were stopped: 0.0468229 seconds<br /></strong></li><li><strong>-XX:PrintHeapAtGC</strong>:打印GC前后的详细堆栈信息<br />输出形式：<br />34.702: [GC {Heap before gc invocations=7:<br />def new generation&nbsp;&nbsp; total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)<br /><strong>eden space 49152K, 99% used</strong> [0x1ebd0000, 0x21bce430, 0x21bd0000)<br /><strong>from space 6144K, 55% used</strong> [0x221d0000, 0x22527e10, 0x227d0000)<br />to&nbsp;&nbsp; space 6144K,&nbsp;&nbsp; 0% used [0x21bd0000, 0x21bd0000, 0x221d0000)<br />tenured generation&nbsp;&nbsp; total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)<br /><strong>the space 69632K,&nbsp;&nbsp; 3% used</strong> [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)<br />compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)<br />&nbsp;&nbsp; the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)<br />&nbsp;&nbsp;&nbsp; ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)<br />&nbsp;&nbsp;&nbsp; rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)<br />34.735: [DefNew: 52568K-&gt;3433K(55296K), 0.0072126 secs] 55264K-&gt;6615K(124928K)<strong>Heap after gc invocations=8:<br /></strong>def new generation&nbsp;&nbsp; total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)<br /><strong>eden space 49152K,&nbsp;&nbsp; 0% used</strong> [0x1ebd0000, 0x1ebd0000, 0x21bd0000)<br />from space 6144K, 55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)<br />to&nbsp;&nbsp; space 6144K,&nbsp;&nbsp; 0% used [0x221d0000, 0x221d0000, 0x227d0000)<br />tenured generation&nbsp;&nbsp; total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)<br /><strong>the space 69632K,&nbsp;&nbsp; 4% used </strong>[0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)<br />compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)<br />&nbsp;&nbsp; the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)<br />&nbsp;&nbsp;&nbsp; ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)<br />&nbsp;&nbsp;&nbsp; rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)<br />}<br />, 0.0757599 secs] </li><li><strong>-Xloggc:filename</strong>:与上面几个配合使用，把相关日志信息记录到文件以便分析。</li></ul></li><li><strong>常见配置汇总</strong> 
<ol><li>堆设置 
<ul><li><strong>-Xms</strong>:初始堆大小 </li><li><strong>-Xmx</strong>:最大堆大小 </li><li><strong>-XX:NewSize=n</strong>:设置年轻代大小 </li><li><strong>-XX:NewRatio=n:</strong>设置年轻代和年老代的比值。如:为3，表示年轻代与年老代比值为1：3，年轻代占整个年轻代年老代和的1/4 </li><li><strong>-XX:SurvivorRatio=n</strong>:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如：3，表示Eden：Survivor=3：2，一个Survivor区占整个年轻代的1/5 </li><li><strong>-XX:MaxPermSize=n</strong>:设置持久代大小</li></ul></li></ol></li><li>收集器设置 
<ul><li><strong>-XX:+UseSerialGC</strong>:设置串行收集器 </li><li><strong>-XX:+UseParallelGC</strong>:设置并行收集器 </li><li><strong>-XX:+UseParalledlOldGC</strong>:设置并行年老代收集器 </li><li><strong>-XX:+UseConcMarkSweepGC</strong>:设置并发收集器</li></ul></li><li>垃圾回收统计信息 
<ul><li><strong>-XX:+PrintGC</strong> </li><li><strong>-XX:+PrintGCDetails</strong> </li><li><strong>-XX:+PrintGCTimeStamps</strong> </li><li><strong>-Xloggc:filename</strong></li></ul></li><li>并行收集器设置 
<ul><li><strong>-XX:ParallelGCThreads=n</strong>:设置并行收集器收集时使用的CPU数。并行收集线程数。 </li><li><strong>-XX:MaxGCPauseMillis=n</strong>:设置并行收集最大暂停时间 </li><li><strong>-XX:GCTimeRatio=n</strong>:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)</li></ul></li><li>并发收集器设置 
<ul><li><strong>-XX:+CMSIncrementalMode</strong>:设置为增量模式。适用于单CPU情况。 </li><li><strong>-XX:ParallelGCThreads=n</strong>:设置并发收集器年轻代收集方式为并行收集时，使用的CPU数。并行收集线程数。</li></ul></li>
<p><strong>四、调优总结</strong> </p>
<ol><li><strong>年轻代大小选择</strong> 
<ul><li><strong>响应时间优先的应用</strong>：<strong>尽可能设大，直到接近系统的最低响应时间限制</strong>（根据实际情况选择）。在此种情况下，年轻代收集发生的频率也是最小的。同时，减少到达年老代的对象。 </li><li><strong>吞吐量优先的应用</strong>：尽可能的设置大，可能到达Gbit的程度。因为对响应时间没有要求，垃圾收集可以并行进行，一般适合8CPU以上的应用。</li></ul></li><li><strong>年老代大小选择</strong> 
<ul><li><strong>响应时间优先的应用</strong>：年老代使用并发收集器，所以其大小需要小心设置，一般要考虑<strong>并发会话率</strong>和<strong>会话持续时间</strong>等一些参数。如果堆设置小了，可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式；如果堆大了，则需要较长的收集时间。最优化的方案，一般需要参考以下数据获得： 
<ul><li>并发垃圾收集信息 </li><li>持久代并发收集次数 </li><li>传统GC信息 </li><li>花在年轻代和年老代回收上的时间比例</li></ul></li></ul></li>减少年轻代和年老代花费的时间，一般会提高应用的效率</li><li><strong>吞吐量优先的应用</strong>：一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是，这样可以尽可能回收掉大部分短期对象，减少中期的对象，而年老代尽存放长期存活对象。</li><li><strong>较小堆引起的碎片问题<br /></strong>因为年老代的并发收集器使用标记、清除算法，所以不会对堆进行压缩。当收集器回收时，他会把相邻的空间进行合并，这样可以分配给较大的对象。但是，当堆空间较小时，运行一段时间以后，就会出现&#8220;碎片&#8221;，如果并发收集器找不到足够的空间，那么并发收集器将会停止，然后使用传统的标记、清除方式进行回收。如果出现&#8220;碎片&#8221;，可能需要进行如下配置： 
<ul><li><strong>-XX:+UseCMSCompactAtFullCollection</strong>：使用并发收集器时，开启对年老代的压缩。 </li><li><strong>-XX:CMSFullGCsBeforeCompaction=0</strong>：上面配置开启的情况下，这里设置多少次Full GC后，对年老代进行压缩</li></ul></li><li>jvm的内存限制</li></ol>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; windows2003是1612M</p></div><br /><br /><a href="http://www.cnblogs.com/edwardlauxh/archive/2010/04/25/1918603.html">http://www.cnblogs.com/edwardlauxh/archive/2010/04/25/1918603.html</a><img src ="http://www.blogjava.net/stevenjohn/aggbug/391310.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2012-11-14 13:48 <a href="http://www.blogjava.net/stevenjohn/archive/2012/11/14/391310.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JVM 环境参数配置</title><link>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391309.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Wed, 14 Nov 2012 05:40:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391309.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/391309.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391309.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/391309.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/391309.html</trackback:ping><description><![CDATA[<pre style="padding-bottom: 0px; widows: 2; text-transform: none; background-color: rgb(255,252,246); text-indent: 0px; margin: 0px 0px 10px; padding-left: 0px; padding-right: 0px; zoom: 1; font: 14px/22px Arial; word-wrap: break-word; white-space: pre-wrap; orphans: 2; letter-spacing: normal; color: rgb(0,0,0); word-spacing: 0px; padding-top: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px" id="best-answer-content" class="reply-text mb10" data-accusearea="aContent">典型JVM参数设置： 

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k 

-Xmx3550m：设置JVM最大可用内存为3550M。 

-Xms3550m：设置JVM促使内存为3550m。此值可以设置与-Xmx相同，以避免每次垃圾回收完成后JVM重新分配内存。 

-Xmn2g：设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m，所以增大年轻代后，将会减小年老代大小。此值对系统性能影响较大，Sun官方推荐配置为整个堆的3/8。 

-Xss128k：设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M，以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下，减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的，不能无限生成，经验值在3000~5000左右。

java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0 

-XX:NewRatio=4:设置年轻代（包括Eden和两个Survivor区）与年老代的比值（除去持久代）。设置为4，则年轻代与年老代所占比值为1：4，年轻代占整个堆栈的1/5 

-XX:SurvivorRatio=4：设置年轻代中Eden区与Survivor区的大小比值。设置为4，则两个Survivor区与一个Eden区的比值为2:4，一个Survivor区占整个年轻代的1/6 

-XX:MaxPermSize=16m:设置持久代大小为16m。 

-XX:MaxTenuringThreshold=0：设置垃圾最大年龄。如果设置为0的话，则年轻代对象不经过Survivor区，直接进入年老代。对于年老代比较多的应用，可以提高效率。如果将此值设置为一个较大值，则年轻代对象会在Survivor区进行多次复制，这样可以增加对象再年轻代的存活时间，增加在年轻代即被回收的概论。</pre><img src ="http://www.blogjava.net/stevenjohn/aggbug/391309.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2012-11-14 13:40 <a href="http://www.blogjava.net/stevenjohn/archive/2012/11/14/391309.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用 Runtime 监控 Java 系统资源</title><link>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391305.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Wed, 14 Nov 2012 05:01:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391305.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/391305.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2012/11/14/391305.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/391305.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/391305.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 这些日子要用爪哇语言（Java）做内存数据中心。于是把用 Java 监控运行环境硬件资源的内容复习了一下。爪哇类库提供了 java.util.Runtim 类，主要负责调用爪哇虚拟机（JavaVM）外部的基层操作系统功能、处理基于一种叫钩子的原理的程序、获取系统资源信息以及控制调试信息生成。本文单独利用其获取系统资源信息的功能。java.util.Runtim 类具有以下几个方法和获取系统资源信息...&nbsp;&nbsp;<a href='http://www.blogjava.net/stevenjohn/archive/2012/11/14/391305.html'>阅读全文</a><img src ="http://www.blogjava.net/stevenjohn/aggbug/391305.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2012-11-14 13:01 <a href="http://www.blogjava.net/stevenjohn/archive/2012/11/14/391305.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java内存溢出 </title><link>http://www.blogjava.net/stevenjohn/archive/2012/10/25/390240.html</link><dc:creator>abin</dc:creator><author>abin</author><pubDate>Thu, 25 Oct 2012 15:16:00 GMT</pubDate><guid>http://www.blogjava.net/stevenjohn/archive/2012/10/25/390240.html</guid><wfw:comment>http://www.blogjava.net/stevenjohn/comments/390240.html</wfw:comment><comments>http://www.blogjava.net/stevenjohn/archive/2012/10/25/390240.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/stevenjohn/comments/commentRss/390240.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/stevenjohn/services/trackbacks/390240.html</trackback:ping><description><![CDATA[<div id="article_content" class="article_content">
<p>内存溢出与数据库锁表的问题，可以说是开发人员的噩梦，一般的程序异常，总是可以知道在什么时候或是在什么操作步骤上出现了异常，而且根据堆栈信息也很容易定位到程序中是某处出现了问题。内存溢出与锁表则不然，一般现象是操作一般时间后系统越来越慢，直到死机，但并不能明确是在什么操作上出现的，发生的时间点也没有规律，<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>日志或<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>数据库也不能定位出问题的代码。</p>
<p>更严重的是内存溢出与数据库锁表在系统开发和单元测试阶段并不容易被发现，当系统正式上线一般时间后，操作的并发量上来了，数据也积累了一些，系统就容易出现内存溢出或是锁表的现象，而此时系统又不能随意停机或重启，为修正BUG带来很大的困难。</p>
<p>本文以笔者开发和支持的多个项目为例，与大家分享在开发过程中遇到的Java内存溢出和数据库锁表的检测和处理解决过程。</p>
<p>2．内存溢出的分析<br />内存溢出是指应用系统中存在无法回收的内存或使用的内存过多，最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。为了解决Java中内存溢出问题，我们首先必须了解Java是如何管理内存的。Java的内存管理就是对象的分配和释放问题。在Java中，内存的分配是由程序完成的，而内存的释放是由垃圾收集器(Garbage Collection，GC)完成的，程序员不需要通过调用GC函数来释放内存，因为不同的JVM实现者可能使用不同的算法管理GC，有的是内存使用到达一定程度时，GC才开始工作，也有定时执行的，有的是中断式执行GC。但GC只能回收无用并且不再被其它对象引用的那些对象所占用的空间。Java的内存垃圾回收机制是从程序的主要运行对象开始检查引用链，当遍历一遍后发现没有被引用的孤立对象就作为垃圾回收。</p>
<p>引起内存溢出的原因有很多种，常见的有以下几种：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内存中加载的数据量过于庞大，如一次从数据库取出过多数据；</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 集合类中有对对象的引用，使用完后未清空，使得JVM不能回收；</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 代码中存在死循环或循环产生过多重复的对象实体；</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用的第三方软件中的BUG；</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 启动参数内存值设定的过小；</p>
<p>3．内存溢出的解决<br />内存溢出虽然很棘手，但也有相应的解决办法，可以按照从易到难，一步步的解决。</p>
<p>第一步，就是修改JVM启动参数，直接增加内存。这一点看上去似乎很简单，但很容易被忽略。JVM默认可以使用的内存为64M，Tomcat默认可以使用的内存为128MB，对于稍复杂一点的系统就会不够用。在某项目中，就因为启动参数使用的默认值，经常报&#8220;OutOfMemory&#8221;错误。因此，-Xms，-Xmx参数一定不要忘记加。</p>
<p>第二步，检查错误日志，<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>&#8220;OutOfMemory&#8221;错误前是否有其它异常或错误。在一个项目中，使用两个数据库连接，其中专用于发送短信的数据库连接使用DBCP连接池管理，用户为不将短信发出，有意将数据库连接用户名改错，使得日志中有许多数据库连接异常的日志，一段时间后，就出现&#8220;OutOfMemory&#8221;错误。经分析，这是由于DBCP连接池BUG引起的，数据库连接不上后，没有将连接释放，最终使得DBCP报&#8220;OutOfMemory&#8221;错误。经过修改正确数据库连接参数后，就没有再出现内存溢出的错误。</p>
<p><span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>日志对于分析内存溢出是非常重要的，通过仔细<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>日志，分析内存溢出前做过哪些操作，可以大致定位有问题的模块。</p>
<p>第三步，安排有经验的编程人员对代码进行走查和分析，找出可能发生内存溢出的位置。重点排查以下几点：</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 检查代码中是否有死循环或递归调用。</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 检查是否有大循环重复产生新对象实体。</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 检查对数据库查询中，是否有一次获得全部数据的查询。一般来说，如果一次取十万条记录到内存，就可能引起内存溢出。这个问题比较隐蔽，在上线前，数据库中数据较少，不容易出问题，上线后，数据库中数据多了，一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。</p>
<p>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 检查List、MAP等集合对象是否有使用完后，未清除的问题。List、MAP等集合对象会始终存有对对象的引用，使得这些对象不能被GC回收。</p>
<p>第四步，使用内存<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>工具动态<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>内存使用情况。某个项目上线后，每次系统启动两天后，就会出现内存溢出的错误。这种情况一般是代码中出现了缓慢的内存泄漏，用上面三个步骤解决不了，这就需要使用内存<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>工具了。</p>
<p>内存<span style="background-color: yellow; color: #ee6600" id="Mark">查看</span>工具有许多，比较有名的有：Optimizeit Profiler、JProbe Profiler、JinSight和Java1.5的Jconsole等。它们的基本工作原理大同小异，都是监测Java程序运行时所有对象的申请、释放等动作，将内存管理的所有信息进行统计、分析、可视化。开发人员可以根据这些信息判断程序是否有内存泄漏问题。一般来说，一个正常的系统在其启动完成后其内存的占用量是基本稳定的，而不应该是无限制的增长的。持续地观察系统运行时使用的内存的大小，可以看到在内存使用监控窗口中是基本规则的锯齿形的图线，如果内存的大小持续地增长，则说明系统存在内存泄漏问题。通过间隔一段时间取一次内存快照，然后对内存快照中对象的使用与引用等信息进行比对与分析，可以找出是哪个类的对象在泄漏。</p>
<p>通过以上四个步骤的分析与处理，基本能处理内存溢出的问题。当然，在这些过程中也需要相当的经验与敏感度，需要在实际的开发与调试过程中不断积累。</p>
<p>总体上来说，产生内存溢出是由于代码写的不好造成的，因此提高代码的质量是最根本的解决办法。有的人认为先把功能实现，有BUG时再在测试阶段进行修正，这种想法是错误的。正如一件产品的质量是在生产制造的过程中决定的，而不是质量检测时决定的，软件的质量在设计与编码阶段就已经决定了，测试只是对软件质量的一个验证，因为测试不可能找出软件中所有的BUG。</p>
<p>&nbsp;</p>
<p>--------------------------------------------------------------------------------------------------------------------------------</p>
<p>&nbsp;</p>
<p>原因有很多种，比如：</p>
<p>1.数据量过于庞大；死循环 ；静态变量和静态方法过多；递归；无法确定是否被引用的对象；</p>
<p>2.虚拟机不回收内存（内存泄漏）；</p>
<p>&nbsp;&nbsp;&nbsp; 说白了就是程序运行要用到的内存大于虚拟机能提供的最大内存就发生内存溢出了。 内存溢出的问题要看业务和系统大小而定，对于某些系统可能内存溢出不常见，但某些系统还是很常见的解决的方法，</p>
<p>一个是优化程序代码，如果业务庞大，逻辑复杂，尽量减少全局变量的引用，让程序使用完变量的时候释放该引用能够让垃圾回收器回收，释放资源。<br />二就是物理解决，增大物理内存，然后通过：-Xms256m -Xmx256m -XX:MaxNewSize=256m -XX:MaxPermSize=256m的修改</p>
<p><span style="font-size: small"><strong><font size="2">一、内存溢出类型</font></strong></span> <br />1 、 java.lang.OutOfMemoryError: PermGen space</p>
<p>JVM 管理两种类型的内存，堆和非堆。堆是给开发人员用的上面说的就是，是在 JVM 启动时创建；非堆是留给 JVM 自己用的，用来存放类的信息的。它和堆不同，运行期内 GC 不会释放空间。如果 web app 用了大量的第三方 jar 或者应用有太多的 class 文件而恰好 MaxPermSize 设置较小，超出了也会导致这块内存的占用过多造成溢出，或者 tomcat 热部署时侯不会清理前面加载的环境，只会将 context 更改为新部署的，非堆存的内容就会越来越多。</p>
<p>2 、 java.lang.OutOfMemoryError: Java heap space</p>
<p>第一种情况是个补充，主要存在问题就是出现在这个情况中。其默认空间 ( 即 -Xms) 是物理内存的 1/64 ，最大空间 (-Xmx) 是物理内存的 1/4 。如果内存剩余不到 40 ％， JVM 就会增大堆到 Xmx 设置的值，内存剩余超过 70 ％， JVM 就会减小堆到 Xms 设置的值。所以服务器的 Xmx 和 Xms 设置一般应该设置相同避免每次 GC 后都要调整虚拟机堆的大小。假设物理内存无限大，那么 JVM 内存的最大值跟操作系统有关，一般 32 位机是 1.5g 到 3g 之间，而 64 位的就不会有限制了。</p>
<p>注意：如果 Xms 超过了 Xmx 值，或者堆最大值和非堆最大值的总和超过了物理内存或者操作系统的最大限制都会引起服务器启动不起来。</p>
<p>垃圾回收 GC 的角色</p>
<p>JVM 调用 GC 的频度还是很高的，主要两种情况下进行垃圾回收：</p>
<p>当应用程序线程空闲；另一个是 java 内存堆不足时，会不断调用 GC ，若连续回收都解决不了内存堆不足的问题时，就会报 out of memory 错误。因为这个异常根据系统运行环境决定，所以无法预期它何时出现。</p>
<p>根据 GC 的机制，程序的运行会引起系统运行环境的变化，增加 GC 的触发机会。</p>
<p>为了避免这些问题，程序的设计和编写就应避免垃圾对象的内存占用和 GC 的开销。显示调用 System.GC() 只能建议 JVM 需要在内存中对垃圾对象进行回收，但不是必须马上回收，</p>
<p>一个是并不能解决内存资源耗空的局面，另外也会增加 GC 的消耗。</p>
<p><strong><span style="font-size: small"><font size="2">二、 JVM 内存区域组成</font></span></strong> <br />简单的说 java中的堆和栈</p>
<p>java把内存分两种：一种是栈内存，另一种是堆内存</p>
<p>1。在函数中定义的基本类型变量和对象的引用变量都在函数的栈内存中分配；</p>
<p>2。堆内存用来存放由 new创建的对象和数组</p>
<p>在函数（代码块）中定义一个变量时， java就在栈中为这个变量分配内存空间，当超过变量的作用域后， java会自动释放掉为该变量所分配的内存空间；在堆中分配的内存由 java虚拟机的自动垃圾回收器来管理</p>
<p>堆的优势是可以动态分配内存大小，生存期也不必事先告诉编译器，因为它是在运行时动态分配内存的。缺点就是要在运行时动态分配内存，存取速度较慢；</p>
<p>栈的优势是存取速度比堆要快，缺点是存在栈中的数据大小与生存期必须是确定的无灵活 性。</p>
<p>java 堆分为三个区： New 、 Old 和 Permanent</p>
<p>GC 有两个线程：</p>
<p>新创建的对象被分配到 New 区，当该区被填满时会被 GC 辅助线程移到 Old 区，当 Old 区也填满了会触发 GC 主线程遍历堆内存里的所有对象。 Old 区的大小等于 Xmx 减去 -Xmn</p>
<p>java栈存放</p>
<p>栈调整：参数有 +UseDefaultStackSize -Xss256K，表示每个线程可申请 256k的栈空间</p>
<p>每个线程都有他自己的 Stack</p>
<p><strong><span style="font-size: small"><font size="2">三、 JVM如何设置虚拟内存 <br /></font></span></strong>提示：在 JVM中如果 98％的时间是用于 GC且可用的 Heap size 不足 2％的时候将抛出此异常信息。</p>
<p>提示： Heap Size 最大不要超过可用物理内存的 80％，一般的要将 -Xms和 -Xmx选项设置为相同，而 -Xmn为 1/4的 -Xmx值。</p>
<p>提示： JVM初始分配的内存由 -Xms指定，默认是物理内存的 1/64； JVM最大分配的内存由 -Xmx指定，默认是物理内存的 1/4。</p>
<p>默认空余堆内存小于 40%时， JVM就会增大堆直到 -Xmx的最大限制；空余堆内存大于 70%时， JVM会减少堆直到 -Xms的最小限制。因此服务器一般设置 -Xms、 -Xmx相等以避免在每次 GC 后调整堆的大小。</p>
<p>提示：假设物理内存无限大的话， JVM内存的最大值跟操作系统有很大的关系。</p>
<p>简单的说就 32位处理器虽然可控内存空间有 4GB,但是具体的操作系统会给一个限制，</p>
<p>这个限制一般是 2GB-3GB（一般来说 Windows系统下为 1.5G-2G， Linux系统下为 2G-3G）， 而 64bit以上的处理器就不会有限制了</p>
<p>提示：注意：如果 Xms超过了 Xmx值，或者堆最大值和非堆最大值的总和超过了物理内 存或者操作系统的最大限制都会引起服务器启动不起来。</p>
<p>提示：设置 NewSize、 MaxNewSize相等， &#8220;new&#8221;的大小最好不要大于 &#8220;old&#8221;的一半，原因是 old区如果不够大会频繁的触发 &#8220;主 &#8221; GC ，大大降低了性能</p>
<p>JVM使用 -XX:PermSize设置非堆内存初始值，默认是物理内存的 1/64；</p>
<p>由 XX:MaxPermSize设置最大非堆内存的大小，默认是物理内存的 1/4。</p>
<p>解决方法：手动设置 Heap size</p>
<p>修改 TOMCAT_HOME/bin/catalina.bat</p>
<p>在&#8220; echo &#8220;Using CATALINA_BASE: $CATALINA_BASE&#8221;&#8221;上面加入以下行：</p>
<div>
<div class="dp-highlighter">
<ol class="dp-j"><li class="alt"><span>JAVA_OPTS=&#8221;-server -Xms800m -Xmx800m -XX:MaxNewSize=256m&#8221;&nbsp;&nbsp;&nbsp;</span> </li></ol></div></div>
<p><strong><span style="font-size: small"><font size="2">四、性能检查工具使用</font></span></strong> <br />定位内存泄漏：</p>
<p>JProfiler 工具主要用于检查和跟踪系统（限于 Java 开发的）的性能。 JProfiler 可以通过时时的监控系统的内存使用情况，随时监视垃圾回收，线程运行状况等手段，从而很好的监视 JVM 运行情况及其性能。</p>
<p><br />1. 应用服务器内存长期不合理占用，内存经常处于高位占用，很难回收到低位；</p>
<p>2. 应用服务器极为不稳定，几乎每两天重新启动一次，有时甚至每天重新启动一次；</p>
<p>3. 应用服务器经常做 Full GC(Garbage Collection)，而且时间很长，大约需要 30-40秒，应用服务器在做 Full GC的时候是不响应客户的交易请求的，非常影响系统性能。</p>
<p>因为开发环境和产品环境会有不同，导致该问题发生有时会在产品环境中发生， 通常可以使用工具跟踪系统的内存使用情况，在有些个别情况下或许某个时刻确实 是使用了大量内存导致 out of memory，这时应继续跟踪看接下来是否会有下降，</p>
<p>如果一直居高不下这肯定就因为程序的原因导致内存泄漏。</p>
<p><strong><span style="font-size: small"><font size="2">五、不健壮代码的特征及解决办法</font></span></strong> <br />1 、尽早释放无用对象的引用。好的办法是使用临时变量的时候，让引用变量在退出活动域后，自动设置为 null ，暗示垃圾收集器来收集该对象，防止发生内存泄露。</p>
<p>对于仍然有指针指向的实例， jvm 就不会回收该资源 , 因为垃圾回收会将值为 null 的对象作为垃圾，提高 GC 回收机制效率；</p>
<p>2 、我们的程序里不可避免大量使用字符串处理，避免使用 String ，应大量使用 StringBuffer ，每一个 String 对象都得独立占用内存一块区域；</p>
<div>
<div class="dp-highlighter">
<ol class="dp-j"><li class="alt"><span>String str = &#8220;aaa&#8221;; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>String str2 = &#8220;bbb&#8221;; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;</span></li><li class="alt"><span>String str3 = str + str2;</span><span class="comment">// 假如执行此次之后 str ,str2 以后再不被调用 , 那它就会被放在内存中等待 Java 的 gc 去回收 , 程序内过多的出现这样的情况就会报上面的那个错误 , 建议在使用字符串时能使用 StringBuffer 就不要用 String, 这样可以省不少开销； </span><span>&nbsp;&nbsp;</span> </li></ol></div></div>
<p>3 、尽量少用静态变量，因为静态变量是全局的， GC 不会回收的；</p>
<p>4 、避免集中创建对象尤其是大对象， JVM 会突然需要大量内存，这时必然会触发 GC 优化系统内存环境；显示的声明数组空间，而且申请数量还极大。</p>
<p>这是一个案例想定供大家警戒:</p>
<p>使用jspsmartUpload作文件上传,现在运行过程中经常出现java.outofMemoryError的错误，用top命令看看进程使用情况，发现内存不足2M，花了很长时间，发现是jspsmartupload的问题。把jspsmartupload组件的源码文件（class文件）反编译成Java文件，如梦方醒：</p>
<div>
<div class="dp-highlighter">
<ol class="dp-j"><li class="alt"><span>m_totalBytes = m_request.getContentLength();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></li><li><span>m_binArray = </span><span class="keyword">new</span><span> </span><span class="keyword">byte</span><span>[m_totalBytes];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> </li></ol></div></div>
<p>变量m_totalBytes表示用户上传的文件的总长度，这是一个很大的数。如果用这样大的数去声明一个byte数组，并给数组的每个元素分配内存空间，而且m_binArray数组不能马上被释放，JVM的垃圾回收确实有问题，导致的结果就是内存溢出。</p>
<p>jspsmartUpload为什末要这样作，有他的原因，根据RFC1867的http上传标准，得到一个文件流，并不知道文件流的长度。设计者如果想文件的长度，只有操作servletinputstream一次才知道，因为任何流都不知道大小。只有知道文件长度了，才可以限制用户上传文件的长度。为了省去这个麻烦，jspsmartUpload设计者直接在内存中打开文件，判断长度是否符合标准，符合就写到服务器的硬盘。这样产生内存溢出，这只是我的一个猜测而已。</p>
<p>所以编程的时候，不要在内存中申请大的空间，因为web服务器的内存有限，并且尽可能的使用流操作，例如</p>
<div>
<div class="dp-highlighter">
<ol class="dp-j"><li class="alt"><span class="keyword">byte</span><span>[] mFileBody = </span><span class="keyword">new</span><span> </span><span class="keyword">byte</span><span>[</span><span class="number">512</span><span>]; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Blob vField= rs.getBlob(</span><span class="string">"FileBody"</span><span>); &nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InputStream instream=vField.getBinaryStream(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileOutputStream fos=</span><span class="keyword">new</span><span> FileOutputStream(saveFilePath+CFILENAME); &nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span> b; &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>( (b =instream.read(mFileBody)) != -</span><span class="number">1</span><span>){ &nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fos.write(mFileBody,</span><span class="number">0</span><span>,b); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;</span></li><li class="alt"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fos.close(); &nbsp;&nbsp;</span></li><li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instream.close();&nbsp;&nbsp;</span> </li></ol></div></div>
<p>5 、尽量运用对象池技术以提高系统性能；生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏，例如大集合对象拥有大数据量的业务对象的时候，可以考虑分块进行处理，然后解决一块释放一块的策略。</p>
<p>6 、不要在经常调用的方法中创建对象，尤其是忌讳在循环中创建对象。可以适当的使用 hashtable ， vector 创建一组对象容器，然后从容器中去取那些对象，而不用每次 new 之后又丢弃</p>
<p>7 、一般都是发生在开启大型文件或跟数据库一次拿了太多的数据，造成 Out Of Memory Error 的状况，这时就大概要计算一下数据量的最大值是多少，并且设定所需最小及最大的内存空间值。</p></div> <img src ="http://www.blogjava.net/stevenjohn/aggbug/390240.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/stevenjohn/" target="_blank">abin</a> 2012-10-25 23:16 <a href="http://www.blogjava.net/stevenjohn/archive/2012/10/25/390240.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>