有时候程序会碰到java.lang.OutOfMemoryError,这个主要是JVM参数没有配好引发的。html
OutOfMemoryError分两种:java.lang.OutOfMemoryError: Java heap space和java.lang.OutOfMemoryError: PermGen space。java
前者是有关堆内存的内存溢出,能够经过配置-Xms和-Xmx参数来解决。web
后者是有关非堆内存的内存溢出,能够经过配置-XX:PermSize和-XX:MaxPermSize来设置。数组
打开eclipse的eclipse.ini会看到以下参数:缓存
-vmargsbash
-Xms128M服务器
-Xmx512Moracle
-XX:PermSize=64Meclipse
-XX:MaxPermSize=128Mjvm
-vmargs:用来讲明后面的就是JVM的参数了
-Xms:JVM初始分配的堆内存
-Xmx:JVM最大容许分配的堆内存,按需分配
-XX:PermSize:JVM初始分配的非堆内存
-XX:MaxPermSize:JVM最大容许分配的非堆内存,按需分配
堆内存:
JVM留给开发者用的内存。通常存放对象以及数组。
JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。
空余堆内存小于40%时,JVM就会增大堆内存直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减小堆内存直到-Xms的最小限制。所以服务器通常设置-Xms、-Xmx相等以免在每次GC后调整堆内存的大小。
若是-Xmx不指定或者指定偏小,应用可能会致使java.lang.OutOfMemory错误,此错误来自JVM,不是Throwable的,没法用try...catch捕捉。
非堆内存:
JVM留给本身用的内存。方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每一个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
JVM初始分配的非堆内存由-XX:PermSize指定,默认是物理内存的1/64;JVM最大分配的非堆内存由-XX:MaxPermSize 指定,默认是物理内存的1/4。(还有一说:MaxPermSize缺省值和-server -client选项相关,-server选项下默认MaxPermSize为64m,-client选项下默认MaxPermSize为32m)
-XX:MaxPermSize设置太小会致使java.lang.OutOfMemoryError: PermGen space,缘由以下:
PermGen space用于存放Class和Meta的信息,GC不会对PermGen space进行处理,因此若是Load不少Class的话,就会出现上述Error。这种Error在web服务器对JSP进行pre compile的时候比较常见
-server and -client
有两种类型的 HotSpot JVM,即”server”和”client”。服务端的VM中的默认为堆提供了一个更大的空间以及一个并行的垃圾收集器,而且在运行时能够更大程度地优 化代码。客户端的VM更加保守一些(校对注:这里做者指客户端虚拟机有较小的默认堆大小),这样能够缩短JVM的启动时间和占用更少的内存。有一个 叫”JVM功效学”的概念,它会在JVM启动的时候根据可用的硬件和操做系统来自动的选择JVM的类型。具体的标准能够在这里找到。从标准表中,咱们能够看到客户端的VM只在32位系统中可用。
若是咱们不喜欢预选(校对注:指JVM自动选择的JVM类型)的JVM,咱们可使 用-server和-client参数来设置使用服务端或客户端的VM。虽然当初服务端VM的目标是长时间运行的服务进程,可是如今看来,在运行独立应用 程序时它比客户端VM有更出色的性能。当应用的性能很是重要时,我推荐使用-server参数来选择服务端VM。一个常见的问题:在一个32位的系统 上,HotSpot JDK能够运行服务端VM,可是32位的JRE只能运行客户端VM。
-version and -showversion
当咱们调用“java”命令时,咱们如何才能知道咱们安装的是哪一个版本的Java和JVM类型呢?在同一个系统中安装多个Java,若是不注意的话有运行错误JVM的风险。在不一样的Linux版本上预装JVM这方面,我认可如今已经变的比之前好不少了。幸运的是,咱们如今可使用-version参数,它能够打印出正在使用的JVM的信息。例如:
$ java -version java version "1.6.0_24" Java(TM) SE Runtime Environment (build 1.6.0_24-b07) Java HotSpot(TM) Client VM (build 19.1-b02, mixed mode, sharing)
输出显示的是Java版本号(1.6.0_24)和JRE确切的build号(1.6.0_24-b07)。咱们也能够看到JVM的名字(HotSpot)、类型(client)和build ID(19.1-b02) )。 除此以外,咱们还知道JVM以混合模式(mixed mode)在运行,这是HotSpot默认的运行模式,意味着JVM在运行时能够动态的把字节码编译为本地代码。咱们也能够看到类数据共享(class data sharing)是开启的,类数据共享(class data sharing)是一种在只读缓存(在jsa文件中,”Java Shared Archive”)中存储JRE的系统类,被全部Java进程的类加载器用来当作共享资源。类数据共享(Class data sharing)可能在常常从jar文档中读全部的类数据的状况下显示出性能优点。
-version参数在打印完上述信息后当即终止JVM。还有一个相似的参数 -showversion能够用来输出相同的信息,可是-showversion紧接着会处理并执行Java程序。所以,-showversion对几乎 全部Java应用的命令行都是一个有效的补充。你永远不知道你何时,忽然须要了解一个特定的Java应用(崩溃时)使用的JVM的一些信息。在启动时添加-showversion,咱们就能保证当咱们须要时能够获得这些信息。
-Xint, -Xcomp, 和 -Xmixed
-Xint和-Xcomp参数和咱们的平常工做不是很相关,可是我很是有兴趣经过它来了解下JVM。在解释模式(interpreted mode)下,-Xint标记会强制JVM执行全部的字节码,固然这会下降运行速度,一般低10倍或更多。- Xcomp参数与它(-Xint)正好相反,JVM在第一次使用时会把全部的字节码编译成本地代码,从而带来最大程度的优化。这听起来不错,由于这彻底绕 开了缓慢的解释器。然而,不少应用在使用-Xcomp也会有一些性能损失,固然这比使用-Xint损失的少,缘由是-xcomp没有让JVM启用JIT编 译器的所有功能。JIT编译器在运行时建立方法使用文件,而后一步一步的优化每个方法,有时候会主动的优化应用的行为。这些优化技术,好比,积极的分支 预测(optimistic branch prediction),若是不先分析应用就不能有效的使用。另外一方面方法只有证实它们与此相关时才会被编译,也就是,在应用中构建某种热点。被调用不多 (甚至只有一次)的方法在解释模式下会继续执行,从而减小编译和优化成本。
注意混合模式也有他本身的参数,-Xmixed。最新版本的HotSpot的默认模式是混合模式,因此咱们不须要特别指定这个标记。咱们来用对象填充HashMap而后检索它的结果作一个简单的用例。每个例子,它的运行时间都是不少次运行的平均时间。
$ java -server -showversion Benchmark java version "1.6.0_24" Java(TM) SE Runtime Environment (build 1.6.0_24-b07) Java HotSpot(TM) Server VM (build 19.1-b02, mixed mode) Average time: 0.856449 seconds
$ java -server -showversion -Xcomp Benchmark java version "1.6.0_24" Java(TM) SE Runtime Environment (build 1.6.0_24-b07) Java HotSpot(TM) Server VM (build 19.1-b02, compiled mode) Average time: 0.950892 seconds
$ java -server -showversion -Xint Benchmark java version "1.6.0_24" Java(TM) SE Runtime Environment (build 1.6.0_24-b07) Java HotSpot(TM) Server VM (build 19.1-b02, interpreted mode) Average time: 7.622285 seconds
固然也有不少使-Xcomp表现很好的例子。特别是运行时间长的应用,我强烈建议你们使用JVM的默认设置,让JIT编译器充分发挥其动态潜力,毕竟JIT编译器是组成JVM最重要的组件之一。事实上,正是由于JVM在这方面的进展才让Java再也不那么慢。
参考连接: