jvm内存深层解析与性能调优

要更好的消化这篇文章,建议先看看https://blog.csdn.net/cdaimadada/article/details/79884414(java内存解析)这篇博文

对于一般而言,java内存被划分为堆和栈(上一篇文章就是讲这个的),但是,实际上内存划分比这个复杂得多!

概述

对于java程序员来说,在虚拟机自动内存管理机制的帮助下,不在需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出的问题,由虚拟机管理内存看似很好,可一旦出现内存泄漏或者溢出额问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会成为一个异常艰难的工作。


java内存划分

java内存按照标准,可以划分为:

1,程序计数器:它是一块较小的内存空间,可以看做是当前线程锁执行的字节码的行号指示器。一个处理器都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间互不影响,独立存储。

2,java虚拟机栈:java虚拟机栈描述的是java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、接口等信息。每一个方法从调用直至完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。局部变量表用于存储编译期可知的各种基本数据类型、对象引用类型,栈内存用于运行线程。

3,本地方法栈:本地方法栈与虚拟机栈所发挥的作用非常类似,不过虚拟机栈为虚拟机执行java方法服务,而本地方法栈则为虚拟机使用Native方法服务

4,java堆:堆内存区域的唯一目的就是存储对象实例

5,方法区:它用于存储已被虚拟机加载的类信息、常量、静态变量等。虽然java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它还有一个别名叫做Non-Heap,目的是和java堆区分开。相对而言,垃圾回收行为在这个区域是比较少出现的,这个区域的内存回收主要针对常量池的回收和对类型的卸载。

5.1,运行时常量池:它是方法区的一部分,用于存放编译器生成的各种字面量和符号引用(关于常量池的详细讲解请看上面我推荐的那篇文章)

其中,方法区和堆是所有线程共享的内存区域;虚拟机栈、本地方法栈、程序计数器是线程私有的内存区域。

有人把java内存按如下划分(注意:少了程序计数器!少了程序计数器!少了程序计数器!)



java堆内存





java堆内存逻辑上分为三块:年轻代、老年代和永久代。但是实际上,只分为两个部分:年轻代和老年代。永久代不属于堆内存!

Eden为年轻代,Old为老年代,Perm为永久代,元空间:MetaSpace

年轻代

从上图可以看到java堆内存分为多个独立的模块,较为广泛的说,java对内存分为年轻代和老年代。

年轻代是所有新对象产生的地方。当年轻代内存空间被用完时,就会触发垃圾回收。这个垃圾回收叫做Minor GC.年轻代被分为三个部分---Enden区和两个Survivor区

当Eden区被对象填满时,就会执行Minor GC也叫Full GC。并把所有存活下来的对象转移到Survivor0区。当Survivor0区也满了时,对象就会去Survivor1区,1区也满了就会去老年代区。

年老代

年老代内存里包括了长期存活的对象和经过多次Minor GC后依然存活下来的对象

通常在老年代内存满的时候进行垃圾回收。老年代的垃圾回收叫做Major GC。Major GC会花费更多的时间。多次进行Major GC就会出现无法响应的错误!

当老年代满的时候就会报OOM异常

Stop the world事件

为什么有这个事件?

当某一个线程需要使用堆中的对象,如果没有这个事件的话,倘若,在此时需要进行垃圾回收,那么就意味着有两者同时操作同一个对象,显然不合理!

所以jvm在设计的时候,当内存满的时候,也就是开始垃圾回收时,触发这个Stop the world事件。它会让应用程序暂停,先进行垃圾回收。

垃圾回收时间取决于垃圾回收策略。这就是为什么有必要去监控垃圾收集和对垃圾收集进行调优。从而避免要求快速响应的应用出现超时错误。

永久代

永久代或者“Perm Gen”包含了JVM需要的应用元数据,这些元数据描述了在应用里使用的类和方法。注意,永久代不是Java堆内存的一部分。

永久代存放JVM运行时使用的类。永久代主要包含了Java SE库的类和方法。永久代的对象在full GC时进行垃圾收集。

方法区

方法区是永久代空间的一部分,并用来存储类型信息(运行时常量和静态变量)和方法代码和构造函数代码。

jvm性能调优

Java 堆内存开关

java提供了大量的内存开关(参数),我们可以用它来设置内存大小和它们的比例。下面是一些常用的开关:


jvm内存调优有两种方式,

第一种:通过Myeclipse开发工具,给jvm设置启动参数,可以进行调优

具体操作:Windows->Preferences->installed jres->edit->然后在default VM arguements添加参数。如下:


设置方式:一般而言,Xms和Xmx大小要一样,这样就能避免GC后调整堆的大小,-XX:PermSize即永久代大小,如果一个项目中的jar包特别多的时候,就可以将PermSize设置得比较大,毕竟永久代主要存储class文件

(1).堆内存分配

JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指 定,默认是物理内存的1/4。默认空余堆内存小于 40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、 -Xmx相等以避免在每次GC 后调整堆的大小。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行堆内存设置,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值,建议堆的最大值设置为可用内存的最大值的80%,毕竟别的地方也需要用到内存嘛。

(2). 非堆内存分配 
也叫永久保存的区域,用于存放Class和Meta信息,Class在被Load的时候被放入该区域。它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理。JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。 GC不会对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS的话,就很可能出现PermGen space错误(即PerGen space out of memory exception)。

测试代码:System.out.println(Runtime.getRuntime().maxMemory());

第二种:


JVM监控工具的使用

在java安装目录下面的bin打开Jconsole,选择一个java程序相关的进程,进入可以看到当前jvm内存以及使用情况,如果发现对内存或者永久代内存不够用,或者是垃圾回收次数较多,就去调整JVM参数,提高应用的程序的性能和吞吐量。

三种内存溢出异常介绍

1. OutOfMemoryError: Java heap space  堆溢出

内存溢出主要存在问题就是出现在这个情况中。当在JVM中如果98%的时间是用于GC且可用的 Heap size 不足2%的时候将抛出此异常信息。一般老年区满的时候就会出现这个错误。

 2. OutOfMemoryError: PermGen space   非堆溢出(永久保存区域溢出)

这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。如果web app用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者tomcat热部署时侯不会清理前面加载的环境,只会将context更改为新部署的,非堆存的内容就会越来越多。

3. OutOfMemoryError: unable to create new native thread.   无法创建新的线程

这种现象比较少见,也比较奇怪,主要是和jvm与系统内存的比例有关。这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。


Java垃圾回收调优应该是提升应用吞吐量的最后一个选择。当你发现应用由于长时间垃圾回收导致应用性能下降、出现超时的时候,应该考虑java垃圾收集调优。

如果在日志里看到java.lang.OutOfMemoryError:PermGen space错误,那么可以尝试使用 -XX:PermGen 和-XX:MaxPermGen JVM选线去监控并增加Perm Gen内存空间。你也可以尝试使用-XX:+CMSClassUploadingEnabled并查看使用CMS垃圾收集器的执行性能。

如果你看到了大量的Full GC操作,那么你也应该尝试增大老年代的内存空间。

链接:

Tomcat中JVM内存溢出及合理配置及maxThreads如何配置:https://www.cnblogs.com/hadoop-dev/p/6006970.html

Java内存与垃圾回收调优:http://www.importnew.com/14086.html

教学视频:
https://ke.qq.com/webcourse/index.html#course_id=237614&term_id=100280262&taid=1626139043012654&vid=d14207bcmjx

猜你喜欢

转载自blog.csdn.net/cdaimadada/article/details/80197649