这次看一些关于JVM内存分析的内容。

两个程序

程序一

首先来看两个程序,这里是程序一:JVMStackTest,看下代码:

package com.zhyea.robin.jvm;public class JVMStackTest {private static int count = 0;private void recur() {++count;recur();System.out.println("recur()方法执行结束");}public static void main(String[] args) {try {new JVMStackTest().recur();} catch (Throwable e) {e.printStackTrace();} finally {System.out.println("程序递归了 " + count + "次");}}
}

这个类很简单了。JVMStackTest的主体是一个recur()方法。recur()方法是一个递归的方法。所谓递归的方法就是在方法体中又调用了方法自身。在recur()方法中引用了一个类成员变量count来记录递归的次数,并且做了一段输出来提示方法执行结束。JVMStackTest的main方法内容也很少,就是创建了一个JVMStackTest的实例并调用recur()方法,同时使用try{}catch(){}来捕获可能发生的异常或错误,最后在程序执行结束前输出recur()方法递归的次数。

这个程序是有些问题的。问题出在recur()方法这里:recur()是一个递归的方法,但是却没有跳出的语句,这意味着recur()方法将无限的递归下去。我们需要认识到一点:资源是有限的,如果某个服务无限次的占用资源却始终不释放资源就必然会导致问题。每次调用recur()方法都会占用一些资源,资源占用得多了就会导致可用资源不足产生异常或错误。运行下这个程序,看看会报什么样的错误:

在递归了1862次以后报了StackOverflowError,即栈溢出错误。请大家先记住这个错误信息,稍后我们会解释为什么会报这个错。

程序二

再来看看程序二,JVMHeapTest:

package com.zhyea.robin.jvm;import java.util.LinkedList;
import java.util.List;public class JVMHeapTest {private static List<byte[]> list = new LinkedList<>();private void loop() {while (true) {list.add(new byte[1024 * 1024]);}}public static void main(String[] args) {try {new JVMHeapTest().loop();} catch (Throwable t) {t.printStackTrace();} finally {System.out.println("list 的长度是:" + list.size());}}
}

这个JVMHeapTest也是很简单了,就是定义了一个List列表型的类成员变量list,通过泛型指定list中存储的单元必须是数组,而后通过loop()方法向list中不停地添加数组实例。每个数组的大小为1024,也就是1KB大小。main方法也和JVMStackTest差不多,就是创建一个类实例来调用loop方法,通过try/catch捕获可能会发生的异常,最后在程序执行结束的时候输出list的长度。

看看loop()方法,很明显地可以看出这个类也是存在问题的:这个类中的loop()方法有一个死循环,尤其是在这个死循环中还在不停向一个类成员列表添加元素。当添加的元素大小总和超过了某个上限后就会报错。具体是什么错误呢,运行一下看看:

正如预料,发生了错误。发生错误时列表的长度是7,报的错是OutOfMemoryError,根据错误信息“Java heap space”可以看出OutOfMemory错误是发生在heap上,也就是堆上。

通过这两个程序我们演示了两种常见的内存错误:StackOverflow和OutOfMemory,即栈溢出和内存溢出。这两个错误的发生有一个共同点:无限制的占用系统某一部分资源(程序一的无限递归,程序二死循环),以至于超过了某个上限。我们需要弄明白的就是这里说的资源是什么资源,而上限又具体是多少。

JVM内存概述

关于资源,我们前面反复提到过几次,主要就是内存。不过相较于我们通常提到的内存,这里说的内存还分得更细一些。我们通常说笔记本有4G内存、8G内存,就是笔记本的机器内存,这个内存被称作是直接内存或物理内存。而我们启动一个Java程序就会从直接内存中获取一块让JVM独立使用的空间,这块空间就可以被称为是JVM内存。而JVM内存又可以被粗略地分成两大类:栈内存和堆内存。如下图:

注意我们这里将内存分成栈内存和堆内存是一种很粗略的分法,至于准确的分法我们稍后会单独说。

看一下上图,左侧的细长条标识栈内存,右侧的椭圆标识内存。通常栈内存要比堆内存小得多。

栈内存

在栈内存中存储的是方法执行的信息,包括局部变量表、操作栈、动态链接、方法出口等信息。不知道有没有注意到栈内存的标识图中还有更细小的小长方形。这个小长方形标识的是栈帧。栈帧是栈内存的最小单位。每执行一个方法的时候都会创建一个栈帧。每调用一个方法至执行完成的过程都对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

说到这里可以回过头来看看我们的第一个程序了。我们现在清楚这个程序是占用了哪些资源导致StackOverflow了。在JVMStackTest中的recur()方法中存在一个无限的递归,这样就会为recur()方法无限地的创建栈帧。当虚拟机无法再创建栈帧时,就会产生StackOverflow错误。

这里还有一点需要说明,某些书中提到过这样的说法:如果线程请求的栈深度大 于虚拟机所允许的深度,将抛出StackOverflowError 异常;如果虚拟机栈可以动态扩展 ,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError 异常。这样的说法是没有错的,但是却是存在一些重叠:因为栈是可以动态扩展的,虚拟机无法继续创建栈帧,到底是超过了栈深度还是栈内存不足呢。这个问题没有定论,不过我测试过多次,都是抛出StackOverflow错误。但是不要忘了,理论上也是可能抛出OutOfMemory错误。

堆内存

通常,可以认为堆内存是JVM内存中最大的一部分,在这里存储着几乎所有的对象实例。堆内存也是垃圾回收管理的主要区域。

我们的第二个程序通过一个死循环不停地向list中添加长度很长的数组占用了大量的堆空间,最后没有足够的空间来存储新的实例就会产生OutOfMemory错误。为什么在这个程序里没有发生垃圾回收呢?是因为我们的对象实例,就是那些数组,是保存在一个类成员list中。这个类成员list始终被引用,其中的内容就无法被回收。关于垃圾回收的内容我们以后会详细解说,这里就不提了。

在堆内存这个区域只会发生OutOfMemory错误。

JVM的一些配置

前面我们说过,产生StackOverflow和OutOfMemory是因为“太多的占用系统资源,超过了一个上限”。我们已经知道系统资源是哪些资源了,那个这个上限是什么呢。这里有三个配置项:

Xss的默认值为1M;

Xms的默认值为直接内存的1/64;

Xmx的默认值为直接内存的1/4。

在IDE里这个配置可以在如下位置完成:

大家可以试着调整这些配置,看看运行的结果是否有些不同。

好了,今天就是这些内容,再见!

##########################

转载于:https://www.cnblogs.com/amunote/p/6788762.html

Java内存分析1 - 从两个程序说起相关推荐

  1. Java内存分析工具MAT(Memory Analyzer Tool)的介绍与使用

    详细介绍了Java内存分析工具MAT(Memory Analyzer Tool)的常见使用方法,MAT可以帮助Java程序员快速进行内存分析,定位问题. MAT(Memory Analyzer Too ...

  2. 注解和反射详细笔记。自定义注解,元注解,内置注解。反射机制,Java Reflection,Java内存分析,反射操作注解,java.lang.reflect.Method,Class

    文章目录 注解 什么是注解 内置注解 元注解 自定义注解 反射机制 静态语言 vs 静态语言 Java Reflection 反射相关的主要API Class类 Java内存分析 创建运行时类的对象 ...

  3. JAVA内存分析:使用JDK自带工具进行内存和CPU分析及垃圾回收

    JAVA内存分析:使用JDK自带工具进行内存和CPU分析及垃圾回收 JAVA内存分析一:基于dump内存溢出快照分析 JAVA内存分析二:idea集成jprofiler查看JVM内存使用情况 JAVA ...

  4. Java内存分析—栈,堆,方法区

    Java内存分析-栈,堆,方法区 一.栈: 1).特点是:自动分配,连续空间,先进后出原则. 2).基本数据类型(一共有八种,char,byte,short,int,long,float,double ...

  5. JAVA内存分析:基于dump内存溢出快照分析

    JAVA内存分析 JAVA内存分析一:基于dump内存溢出快照分析 JAVA内存分析二:idea集成jprofiler查看JVM内存使用情况 JAVA内存分析三:使用JDK自带工具进行内存和CPU分析 ...

  6. Java内存分析工具——jmap

    Java内存分析工具--jmap 平时我们在开发Java应用的时候,会涉及到分析对象内存.内存监控,那么就涉及到jmap这个工具,学习后来介绍一下 能干嘛? jmap 一般可用于: jmap能够打印给 ...

  7. 浅谈java内存分析和垃圾收集器

    目录: 1.内存分析 2.两种垃圾回收机制和原理 3.对垃圾回收机制的简单理解 1.内存分析: (1)栈区: (1)(方法执行的内存模型)也就是说每一个方法执行相关调用都在栈里边,每个方法被调用都会创 ...

  8. java 内存分析之jmap 详细用法完整版(一)

    文章目录 一.jmap下载内存信息命令 二.使用eclipse插件MAT来分析heap profile 前言:当我们遇到内存溢出,想要看详细的内存信息,可以使用java自带工具jmap下载完整的内存信 ...

  9. java 简化判断_简化Java内存分析

    java 简化判断 作为一名典型的Java开发人员,除了遵循关闭连接,流等典型的最佳实践外,我从未监视过应用程序的内存使用情况.最近,我们在JBoss服务器中遇到了一些问题,不得不深入研究内存管理Ja ...

最新文章

  1. .net 开源组件推荐 之 StackExchange
  2. 安装linux系统结果,Linux 系统安装[Redhat]
  3. Vue 组件间的通讯
  4. windows下 conda常用使用指令
  5. 管理mysql表知识点_数据库复习提纲(必考知识点整理)
  6. boost::lambda模块ll_static_cast,ll_dynamic_cast,ll_const_cast,ll_reinterpret_cast的测试程序
  7. kubernetes1.8.4 安装指南 -- 10. 测试nginx服务
  8. 《ASP.NET Core 微服务实战》-- 读书笔记(第4章)
  9. 服务器怎么禁止iis静态文件,如何禁止IIS缓存静态文件
  10. win10下openpose1.5安装
  11. CORS跨域漏洞的学习(防止CSRF漏洞导致的漏洞)
  12. symfony ajax,如何在php或symfony中使用jQuery ajax上传文件
  13. python必备入门代码-学习Python必备的八大知识板块,学好这些你就算入门啦~
  14. python实践答辩ppt_看完这篇Python操作PPT总结,从此使用Python玩转Office全家桶没压力!...
  15. Yahoo! Screwdriver:可扩展的持续集成工具
  16. 2021全国电赛H题回顾
  17. 谷歌浏览器配置微信浏览器_微信网页版 - Chrome社交与通讯插件 - 画夹插件网
  18. 校园wifi免费上网
  19. QT 以资源管理器打开文件夹
  20. 在线影音页面的制作方法

热门文章

  1. Nginx源码分析:3张图看懂启动及进程工作原理
  2. MyCat读写分离-笔记(四)
  3. 软件测试周记录之jmeter
  4. 结对编程作业——四则运算
  5. Java 编程下的同步代码块
  6. C#多线程编程实战(二):线程同步
  7. python学习第二十八节(进程,线程)
  8. 聊一聊IAR的workspace文件组织
  9. 0ctf 2017 kernel pwn knote write up
  10. LiquidCrystal库函数