JVM-内存溢出/泄露及相关工具
what
什么是内存溢出和 内存泄漏 呢?
- 内存溢出:内存空间不足导致;
- 内存泄漏:应该释放的对象没有释放。
拿一个生活中的事情来举例:
张三最近交了一个女朋友小红,女孩是他的初恋。张三全心全意地爱惜这个女孩,此时张三心里装不下其他人,这个有另外一个女孩想进入张三内心进不去,这个时候就是内存溢出(张三的心里装满了小红没有空间装下其他女孩)。
小红虽然也喜欢张三,但是心里还有前男友,这个时候小红无法全心全意对张三,这个就是内存泄漏。
show code (展示什么是内存泄漏)
public class Memory Leak {
private Object[] eles;
private final static int size =;
private int index;
public MemoryLeak() {
this.eles = new Object[size];
}
// 往数组中放入元素
public void put(Object o){
eles[index] = o;
index++;
}
// 从数组中取出(此方放应该是取出不是复制,方法中故意没有对元素进行置空)
public Object take(){
index--;
return eles[index];
}
public static void main(String[] args) {
MemoryLeak ml = new MemoryLeak();
Object o = new Object();
System.out.println(o);
ml.put(o);
Object to = ml.take();
System.out.println(to);
System.out.println("-----------");
}
}
下图为运行结果: 可以看到调用完 take之后 ml中数组中仍然是有数据的,这种情况就是内存溢出
tools
这部分不经常使用的话是记不住的,这里就写一个记录方便以后自己查看
JDK 为我们提供的工具
名称 | 作用 |
jps | 虚拟机进程状况工具 |
jstat | 虚拟机统计信息监视工具 |
jinfo | Java配置信息工具 |
jmap | Java内存映像工具 |
jstack | Java堆栈跟踪工具 |
JConsole | Java监视与管理控制台 |
VisualVM | 多合一故障处理工具 |
jps
列出当前机器上正在运行的 虚拟机 进程
-p :仅仅显示 VM 表示,不显示jar,class, main参数等信息.
-m:输出主函数传入的参数. 下的hello 就是在执行程序时从命令行输入的参数
-l: 输出应用程序主类完整package名称或jar完整名称.
-v: 列出jvm参数, -Xms20m -Xmx50m是启动程序指定的jvm参数
jstat
是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
假设需要每250毫秒查询一次进程91600垃圾收集状况,一共查询20次,那命令应当是:jstat -gc 91600 250 20
常用参数:
- class (类加载器)
- compiler (JIT)
- gc (GC堆状态)
- gccapacity (各区大小)
- gccause (最近一次GC统计和原因)
- gcnew (新区统计)
- gcnewcapacity (新区大小)
- gcold (老区统计)
- gcoldcapacity (老区大小)
- gcpermcapacity (永久区大小)
- gcutil (GC统计汇总)
- printcompilation (HotSpot编译统计)
jinfo
查看和修改虚拟机的参数
jinfo –sysprops 可以查看由System.getProperties()取得的参数
jinfo –flag 未被显式指定的参数的系统默认值
jinfo –flags(注意s)显示虚拟机的参数
jinfo –flag +[参数] 可以增加参数,但是仅限于由java -XX:+PrintFlagsFinal –version查询出来且为manageable的参数
jinfo –flag -[参数] 可以去除参数
虚拟机参数设置: -XX:+PrintGC
public class JinfoDemo {
public static void main(String[] args) {
while (true){
byte [] b = null;
for (int i =; i < 10; i++) {
b = new byte[ * 1024 * 1024];
}
try {
Thread .sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
上述程序查看控制台输出
// 控制台输出 [GC (Allocation Failure) 41912K->2186K(174080K), 0.0005920 secs] [GC (Allocation Failure) 42926K->2186K(172032K), 0.0007325 secs] …
使用jinfo来修改参数 jinfo -flags 152036(pid可以使用jps命令查看)
这里可以看到设置的虚拟机参数信息
执行命令 jinfo -flag +PrintGCDetails 152036 修改PrintGCDetails参数
修改完后查看控制台输出
[GC (Allocation Failure) [PSYoungGen: 28190K->1024K(29696K)] 29353K->2186K(159744K), 0.0008307 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 28190K->0K(29696K)] 29353K->1162K(159744K), 0.0003659 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] …
停掉应用重启后发现控制台输出又变回去了。
jmap/jhat
jmap:用于生成堆转储快照(一般称为heapdump或dump文件)。jmap的作用并不仅仅是为了获取dump文件,它还可以查询 finalize 执行队列、Java堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。和jinfo命令一样,jmap有不少功能在Windows平台下都是受限的,除了生成dump文件的-dump选项和用于查看每个类的实例、空间占用统计的-histo选项在所有操作系统都提供之外,其余选项都只能在Linux/Solaris下使用。
jmap -dump:live,format=b,file=heap.bin <pid>
jhat:jhat dump文件名(jhat尽量不要使用,因为使用起来比较占用资源)
屏幕显示“Server is ready.”的提示后,用户在浏览器中键入就可以访问详情
jstack
(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。
在代码中可以用java.lang.Thread类的getAllStackTraces()方法用于获取虚拟机中所有线程的StackTraceElement对象。使用这个方法可以通过简单的几行代码就完成jstack的大部分功能,在实际项目中不妨调用这个方法做个管理员页面,可以随时使用浏览器来查看线程堆栈。
public class JstackDemo {
public static void main(String[] args) {
while(true){
Map<Thread, StackTraceElement[]> threadMap
= Thread.getAllStackTraces();
for(Map.Entry<Thread, StackTraceElement[]> entry:
threadMap.entrySet()) {
Thread t = entry.getKey();
StackTraceElement[] ss = entry.getValue();
System.out.println(t.getName()+"-"+t.getId());
for(StackTraceElement s:ss) {
System.out.println(s);
}
}
}
}
}
JConsole
*管理远程进程需要在远程程序的启动参数中增加:*
-Djava.rmi.server.hostname=…..
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8888
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
visualvm
懒得搞了,以后再补
Mat工具
eclipse memory analyzer
*浅堆和深堆*
浅堆 :(Shallow Heap)是指一个对象所消耗的内存。例如,在32位系统中,一个对象引用会占据4个字节,一个int类型会占据4个字节,long型变量会占据8个字节,每个对象头需要占用8个字节。
深堆 :这个对象被GC回收后,可以真实释放的内存大小,也就是只能通过对象被 *直接* 或 *间接* 访问到的所有对象的集合。通俗地说,就是指仅被对象所持有的对象的集合。深堆是指对象的保留集中所有的对象的浅堆大小之和。
举例:对象A引用了C和D,对象B引用了C和E。那么对象A的浅堆大小只是A本身,不含C和D,而A的实际大小为A、C、D三者之和。而A的深堆大小为A与D之和,由于对象C还可以通过对象B访问到,因此不在对象A的深堆范围内。