1.查看 gc的次数,和各个垃圾回收区域的内存比例 jstat :
jstat -gcutil pid interval(ms) 例子:jstat -gcutil 332 1000
S1: 新生代中Survivor space 1区已使用空间的百分比
E: 新生代已使用空间的百分比
O: 老年代已使用空间的百分比
P: 永久带已使用空间的百分比
YGC: 从应用程序启动到当前,发生Yang GC 的次数
YGCT: 从应用程序启动到当前,Yang GC所用的时间【单位秒】
FGC: 从应用程序启动到当前,发生Full GC的次数
FGCT: 从应用程序启动到当前,Full GC所用的时间
GCT: 从应用程序启动到当前,用于垃圾回收的总时间【单位秒】
2.内存分析 jmap
2.1 生成堆栈文件
-dump堆到文件,format指定输出格式,live指明是活着的对象,file指定文件名
jmap -dump:live,format=b,file=/data/dump.hprof 21275
2.2 查看jvm 当前配置
jmap -heap 21275
2.3 打印当前内存中对象的数量排序
jmap -histo:live 21275 | more
3.线程分析 jstack
jstack [option] PID
-F : 当正常输出请求不被响应时,强制输出线程堆栈
-l : 除堆栈外,显示关于锁的附加信息
-m : 如果调用到本地方法的话,可以显示C/C++的堆栈
4.生成的服务器,一般按下面进行配置。
8G内存机器:
- JAVA_OPTS="-Djava.awt.headless=true -server -Xms5000m -Xmx5000m -Xmn2500m -=256M -XX:MaxPermSize=512M -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseParNewGC -verbose:gc -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCTimeStamps -Xloggc:/bankapp/tomcat/logs/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/bankapp/tomcat/logs/heapdump"
4G内存机器:
- JAVA_OPTS="-Djava.awt.headless=true -server -Xms2500m -Xmx2500m -Xmn1000m -XX:PermSize=256M -XX:MaxPermSize=512M -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=60 -XX:+UseParNewGC -verbose:gc -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCTimeStamps -Xloggc:/bankapp/tomcat/logs/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/bankapp/tomcat/logs/heapdump"
注意:jdk1.8的元数据区替代了之前的持久代,而且 元数据区占用linux的内存,不占用jvm内存,这时,如果发现linux内存持续飙高,请注意元数据区。
另一个jdk1.8的jvm内存,会缓慢增加,而不会,jvm一启动就会占用 Xms Xmx指定的值,慢慢增加到 Xms Xmx指定的值。
-XX:MinMetaspaceFreeRatio=30 -XX:MaxMetaspaceFreeRatio=60 ?这两个没发现用处。
-XX:MetaspaceSize=5M -XX:MaxMetaspaceSize=512M
5.内存泄漏模拟(jdk1.8)
5.1堆内存溢出
好处,生成的对象大约占用1M,最后滚局i的值,可以算出内存的大小。
import java.util.ArrayList; import java.util.List; public class HeapOomMock { public static void main(String[] args) { List<byte[]> list = new ArrayList<byte[]>(); int i = 0; boolean flag = true; while (flag){ try { i++; list.add(new byte[1024 * 1024]);//每次增加一个1M大小的数组对象 }catch (Throwable e){ e.printStackTrace(); flag = false; System.out.println("count="+i);//记录运行的次数 } } } }
5.1栈溢出
public class StackSOFTest { int depth = 0; public void sofMethod(){ depth ++ ; sofMethod(); } public static void main(String[] args) { StackSOFTest test = null; try { test = new StackSOFTest(); test.sofMethod(); } finally { System.out.println("递归次数:"+test.depth); } } }
5.1元数据区溢出(或者是持久代溢出)
问题:通过jvisualvm 查看内存gc时,发现元数据区的类载fullgc时没有被卸载?如果有人知道,请多多指教
import java.lang.reflect.Method; import org.springframework.cglib.proxy.CallbackFilter; import org.springframework.cglib.proxy.Dispatcher; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; public class OOMTest { public static void main(String[] args) { System.out.println("Let us do it now....."); for(int i=0;i<100000;i++){ Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(TestService.class); enhancer.setCallbackTypes(new Class[] { Dispatcher.class, MethodInterceptor.class }); enhancer.setCallbackFilter(new CallbackFilter() { public int accept(Method method) { return 1; } }); Class clazz = enhancer.createClass(); System.out.println("Time:" + System.currentTimeMillis()); } } }
5.jvisualvm
5.1 jvsiualvm local java applications cannot be monitored
echo %TMP%
查看tmp下面的hsperfdata_username
我解决的方法是 把username全部大写。
5.2 单独jvisualvm 安装
${jdk}/bin/jvisualvm
本地jdk中的jvisualvm启动报错
无法连接 Java VisualVM 插件中心
1
解决方式
舍弃jdk自带jvisualvm,下载独立的。
http://visualvm.github.io/releases.html
根据jdk版本选择
${jvisualvm}/etc/visualvm.conf更改被注释的visualvm_jdkhome="..."为本机jdk绝对路径
启动jvisualvm
工具->插件->设置->插件中心->编辑url
https://visualvm.github.io/pluginscenters.html
根据版本选择.gz放到上文url中