1.配置JVM内存的參数有四个:
-XmxJavaHeap最大值。默认值为物理内存的1/4。最佳设值应该视物理内存大小及计算机内其它内存开销而定。
-XmsJavaHeap初始值,Server端JVM最好将-Xms和-Xmx设为同样值,开发測试机JVM能够保留默认值;
-XmnJavaHeapYoung区大小。不熟悉最好保留默认值。
-Xss每一个线程的Stack大小,不熟悉最好保留默认值;
2.怎样配置JVM内存分配:
(1)当在命令提示符下启动并使用JVM时(仅仅对当前执行的类Test生效):
java-Xmx128m-Xms64m-Xmn32m-Xss16mTest
(2)当在集成开发环境下(如eclipse)启动并使用JVM时:
a.在eclipse根文件夹下打开eclipse.ini,默认内容为(这里设置的是执行当前开发工具的JVM内存分配):
-vmargs-Xms40m-Xmx256m-vmargs表示下面为虚拟机设置參数,可改动当中的參数值,也可加入-Xmn,-Xss。另外,eclipse.ini内还能够设置非堆内存。如:-XX:PermSize=56m。-XX:MaxPermSize=128m。
此处设置的參数值能够通过下面配置在开发工具的状态栏显示:
在eclipse根文件夹下创建文件options。文件内容为:org.eclipse.ui/perf/showHeapStatus=true
改动eclipse根文件夹下的eclipse.ini文件,在开头处加入例如以下内容:
-debugoptions-vmjavaw.exe又一次启动eclipse。就能够看到下方状态条多了JVM信息。
b.打开eclipse-窗体-首选项-Java-已安装的JRE(对在当前开发环境中执行的java程序皆生效)
编辑当前使用的JRE,在缺省VM參数中输入:-Xmx128m-Xms64m-Xmn32m-Xss16m
c.打开eclipse-执行-执行-Java应用程序(仅仅对所设置的java类生效)
选定需设置内存分配的类-自变量。在VM自变量中输入:-Xmx128m-Xms64m-Xmn32m-Xss16m
注:假设在同一开发环境中同一时候进行了b和c设置,则b设置生效。c设置无效。如:
开发环境的设置为:-Xmx256m。而类Test的设置为:-Xmx128m-Xms64m,则执行Test时生效的设置为:
-Xmx256m-Xms64m
(3)当在server环境下(如Tomcat)启动并使用JVM时(对当前server环境下所以Java程序生效):
a.环境变量设置:
变量名:CATALINA_OPTS
变量值:-Xmx128m-Xms64m-Xmn32m-Xss16m
b.打开Tomcat根文件夹下的bin文件夹,编辑catalina.bat,将当中的%CATALINA_OPTS%(共同拥有四处)替换为:-Xmx128m-Xms64m-Xmn32m-Xss16m
二、查看配置JVM内存信息
Runtime.getRuntime().maxMemory();//最大可用内存。相应-Xmx
Runtime.getRuntime().freeMemory();//当前JVM空暇内存
Runtime.getRuntime().totalMemory();//当前JVM占用的内存总数,其值相当于当前JVM已使用的内存及freeMemory()的总和
关于maxMemory(),freeMemory()和totalMemory():
maxMemory()为JVM的最大可用内存,可通过-Xmx设置,默认值为物理内存的1/4。设值不能高于计算机物理内存;
totalMemory()为当前JVM占用的内存总数,其值相当于当前JVM已使用的内存及freeMemory()的总和,会随着JVM使用内存的添加而添加。
freeMemory()为当前JVM空暇内存。由于JVM仅仅有在须要内存时才占用物理内存使用,所以freeMemory()的值普通情况下都非常小。而JVM实际可用内存并不等于freeMemory()。而应该等于maxMemory()-totalMemory()+freeMemory()。及其配置JVM内存分配。
业界有非常多强大的java profile的工具,比方Jporfiler。yourkit,这些收费的东西我就不想说了,想说的是,事实上java自己就提供了非常多内存监控的小工具。以下列举的工具仅仅是一小部分,细致研究下jdk的工具。还是蛮有意思的呢:)
1:gc日志输出
在jvm启动參数中增加 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimestamps -XX:+PrintGCApplicationStopedTime,jvm将会依照这些參数顺序输出gc概要信息,具体信息,gc时间信息,gc造成的应用暂停时间。
假设在刚才的參数后面增加參数 -Xloggc:文件路径。gc信息将会输出到指定的文件里。
其它參数还有
-verbose:gc和-XX:+PrintTenuringDistribution等。
2:jconsole
jconsole是jdk自带的一个内存分析工具。它提供了图形界面。能够查看到被监控的jvm的内存信息,线程信息,类载入信息,MBean信息。
jconsole位于jdk文件夹下的bin文件夹,在windows下是jconsole.exe。在unix和linux下是jconsole.sh,jconsole能够监控本地应用,也能够监控远程应用。
要监控本地应用,运行jconsole pid,pid就是运行的java进程id。假设不带上pid參数。则运行jconsole命令后,会看到一个对话框弹出,上面列出了本地的java进程。能够选择一个进行监控。假设要远程监控。则要在远程server的jvm參数里增加一些东西,由于jconsole的远程监控基于jmx的,关于jconsole具体使用方法,请见专门介绍jconsle的文章,我也会在博客里专门具体介绍jconsole。
3:jviusalvm
在JDK6 update 7之后,jdk推出了另外一个工具:jvisualvm,java可视化虚拟机。它不但提供了jconsole类似的功能。还提供了jvm内存和cpu实时诊断。还有手动dump出jvm内存情况。手动运行gc。
和jconsole一样,执行jviusalvm。在jdk的bin文件夹下执行jviusalvm,windows下是jviusalvm.exe,linux和unix下是jviusalvm.sh。
4:jmap
jmap是jdk自带的jvm内存分析的工具,位于jdk的bin文件夹。
jdk1.6中jmap命令使用方法:
- Usage:
- jmap -histo <pid> (to connect to running process and print histogram of java object heap
- jmap -dump:<dump-options> <pid> (to connect to running process and dump java heap)
- dump-options: format=b binary default file=<file>
- dump heap to <file>
- Example: jmap -dump:format=b,file=heap.bin <pid>
<span style="font-size:18px;">Usage: jmap -histo <pid> (to connect to running process and print histogram of java object heap jmap -dump:<dump-options> <pid> (to connect to running process and dump java heap) dump-options: format=b binary default file=<file> dump heap to <file> Example: jmap -dump:format=b,file=heap.bin <pid> </span>
jmap -histo <pid>在屏幕上显示出指定pid的jvm内存状况。
以我本机为例,运行该命令,屏幕显示:
- 1: 24206 2791864 < constMethodKlass >
- 2: 22371 2145216 [C
- 3: 24206 1940648 < methodKlass >
- 4: 1951 1364496 < constantPoolKlass >
- 5: 26543 1282560 < symbolKlass >
- 6: 6377 1081744 [B
- 7: 1793 909688 < constantPoolCacheKlass >
- 8: 1471 614624 < instanceKlassKlass >
- 9: 14581 548336 [Ljava.lang.Object;
- 10: 3863 513640 [I
- 11: 20677 496248 java.lang.String
- 12: 3621 312776 [Ljava.util.HashMap$Entry;
- 13: 3335 266800 java.lang.reflect.Method
- 14: 8256 264192 java.io.ObjectStreamClass$WeakClassKey
- 15: 7066 226112 java.util.TreeMap$Entry
- 16: 2355 173304 [S
- 17: 1687 161952 java.lang.Class
- 18: 2769 150112 [[I
- 19: 3563 142520 java.util.HashMap
- 20: 5562 133488 java.util.HashMap$Entry
- Total 239019 17140408
<span style="font-size:18px;">1: 24206 2791864 < constMethodKlass > 2: 22371 2145216 [C 3: 24206 1940648 < methodKlass > 4: 1951 1364496 < constantPoolKlass > 5: 26543 1282560 < symbolKlass > 6: 6377 1081744 [B 7: 1793 909688 < constantPoolCacheKlass > 8: 1471 614624 < instanceKlassKlass > 9: 14581 548336 [Ljava.lang.Object; 10: 3863 513640 [I 11: 20677 496248 java.lang.String 12: 3621 312776 [Ljava.util.HashMap$Entry; 13: 3335 266800 java.lang.reflect.Method 14: 8256 264192 java.io.ObjectStreamClass$WeakClassKey 15: 7066 226112 java.util.TreeMap$Entry 16: 2355 173304 [S 17: 1687 161952 java.lang.Class 18: 2769 150112 [[I 19: 3563 142520 java.util.HashMap 20: 5562 133488 java.util.HashMap$Entry Total 239019 17140408 </span>
为了方便查看,我删掉了一些行。从上面的信息非常easy看出,#instance指的是对象数量。#bytes指的是这些对象占用的内存大小,class name指的是对象类型。
再看jmap的dump选项,这个选项是将jvm的堆中内存信息输出到一个文件里,在我本机运行
jmap -dump:file=c:dump.txt 340
注意340是我本机的java进程pid。dump出来的文件比較大有10几M,并且我仅仅是开了tomcat。跑了一个非常easy的应用,且没有不论什么訪问,能够想象,大型繁忙的server上,dump出来的文件该有多大。须要知道的是,dump出来的文件信息是非常原始的,绝不适合人直接观看。而jmap -histo显示的内容又太简单,比如仅仅显示某些类型的对象占用多大内存。以及这些对象的数量,可是没有更具体的信息,比如这些对象各自是由谁创建的。那这么说,dump出来的文件有什么用呢?当然实用,由于有专门分析jvm的内存dump文件的工具。
5:jhat
上面说了,有非常多工具都能分析jvm的内存dump文件。jhat就是sun jdk6及以上版本号自带的工具,位于jdk的bin文件夹,运行 jhat -J -Xmx512m [file] ,file就是dump文件路径。jhat内置一个简单的webserver,此命令运行后,jhat在命令行里显示分析结果的訪问地址,能够用-port选项指定端口,详细使用方法能够运行jhat -heap查看帮助信息。訪问指定地址后。就能看到页面上显示的信息,比jmap -histo命令显示的丰富得多。更为具体。
6:eclipse内存分析器
上面说了jhat。它能分析jvm的dump文件,可是所有是文字显示,eclipse memory analyzer,是一个eclipse提供用于分析jvm 堆dump的插件,网址为 http://www.eclipse.org/mat ,它的分析速度比jhat快,分析结果是图形界面显示。比jhat的可读性更高。
事实上jvisualvm也能够分析dump文件,也是有图形界面显示的。
7:jstat
假设说jmap倾向于分析jvm内存中对象信息的话。那么jsta就是倾向于分析jvm内存的gc情况。都是jvm内存分析工具,但显然,它们是从不同维度来分析的。jsat经常使用的參数有非常多,如 -gc,-gcutil,-gccause,这些选项详细作用可查看jsat帮助信息,我经经常使用-gcutil,这个參数的作用不断的显示当前指定的jvm内存的垃圾收集的信息。
我在本机运行 jstat -gcutil 340 10000,这个命令是每一个10秒钟输出一次jvm的gc信息。10000指的是间隔时间为10000毫秒。
屏幕上显演示样例如以下信息(我仅仅取了第一行,由于是按的一定频率显示。所以实际运行的时候,会有非常多行):
S0 S1 E O P YGC YGCT FGC FGCT GCT
54.62 0.00 42.87 43.52 86.24 1792 5.093 33 7.670 12.763
额。。
。
怎么说呢。要看懂这些信息代表什么意思。还必须对jvm的gc机制有一定的了解才行啊。事实上假设对sun的 hot spot jvm的gc比較了解的人,应该非常easy看懂这些信息,可是不清楚gc机制的人,有点莫名其妙。所以在这里我还是先讲讲sun的jvm的gc机制吧。说到gc,事实上不只不过java的概念,事实上在java之前,就有非常多语言有gc的概念了,gc嘛就是垃圾收集的意思。很多其它的是一种算法性的东西,而跟详细语言没太大关系,所以关于gc的历史,gc的主流算法我就不讲了,那扯得太远了。扯得太远了就是扯淡。
sun如今的jvm,内存的管理模型是分代模型。所以gc当然是分代收集了。分代是什么意思呢?就是将对象依照生命周期分成三个层次。各自是:新生代,旧生代,持久代。
对象刚開始分配的时候。大部分都在新生代。当新生代gc提交被触发后了,运行一次新生代范围内的gc,这叫minor gc,假设运行了几次minor gc后。还有对象存活,将这些对象转入旧生代。由于这些对象已经经过了组织的重重考验了哇。
旧生代的gc频率会更低一些,假设旧生代运行了gc,那就是full gc,由于不是局部gc,而是全内存范围的gc,这会造成应用停顿,由于全内存收集,必须封锁内存,不许有新的对象分配到内存,持久代就是一些jvm期间,基本不会消失的对象,比如class的定义,jvm方法区信息。比如静态块。
须要基本的是。新生代里又分了三个空间:eden,susvivor0。susvivor1。按字面上来理解。就是伊甸园区,幸存1区。幸存2区。新对象分配在eden区中,eden区满时,採用标记-复制算法。即检查出eden区存活 的对象,并将这些对象拷贝到是s0或s1中,然后清空eden区。jvm的gc说开来,不仅仅是这么简单,比如还有串行收集。并行收集,并发收集。还有著名的火车算法,只是那说得太远了,如今对这个有大致了解就好。
讲到这里,再来看一下上面输出的信息:
S0 S1 E O P YGC YGCT FGC FGCT GCT
54.62 0.00 42.87 43.52 86.24 1792 5.093 33 7.670 12.763
S0:新生代的susvivor0区,空间使用率为54..62%
S1:新生代的susvivor1区,空间使用率为0.00%(由于还没有运行第二次minor收集)
E:eden区,空间使用率42.87%
O:旧生代。空间使用率43.52%
P:持久带,空间使用率86.24%
YGC:minor gc运行次数1792次
YGCT:minor gc耗费的时间5.093毫秒
FGC:full gc运行次数33
FGCT:full gc耗费的时间7.670毫秒
GCT:gc耗费的总时间12.763毫秒
如何选择工具
上面列举的一些工具,各有利弊。事实上假设在开发环境,使用什么样的工具是无所谓的,仅仅要能得到结果就好。可是在生产环境里,却不能乱选择,由于这些工具本身就会耗费大量的系统资源。假设在一个生产server压力非常大的时候,贸然运行这些工具,可能会造成非常意外的情况。最好不要在server本机监控。远程监控会比較好一些,可是假设要远程监控。server端的启动脚本要增加一些jvm參数。比如用jconsloe远程监控tomcat或jboss等,都须要设置jvm的jmx參数,假设仅仅仅仅是分析server的内存分配和gc信息,强烈推荐,先用jmap导出server端的jvm的堆dump文件。然后再用jhat,或者jvisualvm。或者eclipse内存分析器来分析内存状况。