`
youyu4
  • 浏览: 427271 次
社区版块
存档分类
最新评论

JVM之决定堆大小以及内存占用

 
阅读更多

JVM之决定堆大小以及内存占用

 

      首先需要判断出应用存活的数据的大小,存活数据的大小是决定配置应用需要的Java堆大小的重要条件,也能够决定是否需要重新审视一下应用的内存需求或者修改应用程序以满足内存需求。

 

注意:存活数据是指,应用处于稳定运行状态下,在Java堆里面长期存活的对象。换一句话说,就是应用在稳定运行的状态下,Full GC之后,Java堆的所占的空间。

 

 

 

约束

   

有多少物理内存可以供JVM使用?是部署多个JVM或者单个JVM?对做出的决定有重要影响。下面列出了一些要点可以帮助决定有多少物理内存可以供使用。

 

  • 一个机器上面只是部署一个JVM,且就一个应用使用?如果是这种情况,那么机器的所有物理内存可以供JVM使用。
  • 一个机器上部署了多个JVM?或者一个机器上部署了多个应用?如果是这两个中的任何一种情况,你就必须要决定每一个JVM或者应用需要分配多少内存了。

注意:无论是前面的哪种情况,都需要给操作系统留出一些内存。

 

 

 

HotSpot VM的堆结构

   

在做内存占用测量之前,我们必须要先理解HotSpot VM Java堆的结构,理解这个对决定应用需要的Java堆大小以及优化垃圾收器性能有很好的帮助。

 



 

HotSpot VM有3个主要的空间:young代、old代以及permanent代,如上图所示。

 

当Java应用分配Java对象时,这些对象被分配到young代。在经历过几次minor GC之后,如果对象还是存活的,就会被转移到old代空间。permanent代空间存储了VM和Java类的元数据比如内置的字符串和类的静态变量。

 

 

配置堆大小

 

  • -Xms:堆初始值大小
  • -Xmx:堆最大值大小

注意:

当-Xms的值小于-Xmx的值的时候,Java堆的大小可以在最大值和最小值之前浮动。当Java应用强调吞吐量和延迟的时候,倾向于把-Xms和-Xmx设置成相同的值,由于调整young代或者old代的大小都需要进行Full GC,Full GC降低吞吐量以及加强延迟。

 

 

年轻代大小

 

  • -XX:NewSize=<n>[g|m|k]:年轻代初始值和最小值的大小,如果设置这个值,就一定要设置-XX:MaxNewSize=<n>[g|m|k]
  • -XX:MaxNewSize=<n>[g|m|k]:年轻代最大值
  • -Xmn<n>[g|m|k]:如果年轻代大小固定,就用这个配置

注意:

如果-Xms和-Xmx没有被设定成相同的值,而且-Xmn被使用了,当调整Java堆的大小的时候,不会调整young代的空间大小,young代的空间大小会保持恒定。因此,-Xmn应该在-Xms和-Xmx设定成相同的时候才指定。  

 

 

老年代大小

 

      old代的空间大小可以基于young代的大小进行计算,old代的初始值的大小是-Xms的值减去-XX:NewSize,最大值是-Xmx减去-XX:MaxNewSize,如果-Xmx和-Xms设置成了相同的值,而且使用-Xmn选项或者-XX:NewSize和-XX:MaxNewSize设置成了相同的值,那么old代的大小就是-Xmx减去-Xmn。

 

 

方法区大小

 

  • -XX:PermSize=<n>[g|m|k]:方法区初始值大小
  • -XX:MaxPermSize=<n>[g|m|k]:方法区最大值大小

注意:

Java应用应该指定这两个值成为同一个值,由于这个值的调整会导致Full GC。

 

 

 

垃圾回收相关信息

 

如果上面提到的Java堆大小、young代、permanent代的大小都没有指定,那么JVM会根据应用的情况自行计算。

 

在young代、old代以及permanent代中任何一个空间里面无法分配对象的时候就会触发垃圾回收,理解这点,对后面的优化非常重要。当young代没有足够空间分配Java对象的时候,触发minor GC。minor GC相对于Full GC来说会更短暂。

 

一个对象在经历过一定次数的Minor GC之后,如果还存活,那么会被转移到old代(对象有一个“任期阀值”的概念,优化延迟的时候再介绍)。当old代没有足够空间放置对象的时候,HotSpot VM触发full GC。实际上在进行Minor GC的时候发现没有old代足够的空间来进行对象的转移,就会触发Full GC,相对于在Minor GC的过程中发现对象的移动失败了然后触发Full GC,这样的策略会有更小的花费。当permanent代的空间不够用的时候的,也会触发Full GC。

 

如果Full GC是由于old代满了而触发的,old代和permanent代的空间都会被垃圾回收,即使permanent代的空间还没有满。同理,如果Full GC是由于permanent代满了而触发的,old代和permanent代的空间都会被垃圾回收,即使old代的空间还没有满。另外,young代同样会被垃圾回收,除非-XX:+ScavengeBeforeFullGC选项被指定了,-XX:+ScavengeBeforeFullGC关闭FullGC的时候young代的垃圾回收。

 

 

 

堆大小优化的起点

 

主要分以下步骤:

 

  • 确定初始的垃圾回收器
  • 自行配置堆大小或让JVM自行选择
  • 监控GC日志
  • 让系统进入稳定运行状态,并观察OutOfMemoryError的情况

 

确定初始的垃圾回收器

 

      就像在“选择JVM runtime”小节里面提到过的,由吞吐量垃圾回收器(throughput garbage collector)开始。记住,使用吞吐量垃圾回收器通过设置-XX:+UserParallelOldGC命令行选项,如果你使用的HotSpot VM不支持的这个选项,那么就使用-XX:+UserParallelGC。

 

 

自行配置堆大小或让JVM自行选择

 

      如果你能够准确的预估到应用需要消耗的Java堆空间,可以通过设定-Xmx和-Xms来作为这个步骤的起点。如果你不知道该设定什么值,就让JVM来选择吧,反正后面,都会根据实际情况进行优化调整。

 

 

监控GC日志

 

      关于如何监控GC日志前面的“GC优化基础”已经描述过了。GC日志会展示在使用中的java堆的大小。初始化和最大的堆大小可以通过-XX:+PrintCommandLineFlags来查看。-XX:+PrintCommandLineFlags打印出在HotSpot VM初始化的时候选择的初始值和最大值比如-XX:InitialHeapSize=<n> -XX:MaxHeapSize=<m>,这里n表示初始化的java堆大小值,m表示java堆的最大值。

 

 

让系统进入稳定运行状态,并观察OutOfMemoryError的情况

 

      不管你是指定java堆的大小还是使用默认的大小,必须让应用进入稳定运行的状态,你必须要有能力和手段让应用处于和线上稳定运行的状态相同的状态。

 

      如果在企图让应用进入稳定状态的时候,你在垃圾回收日志里面观察到OutOfMemoryError,注意是old代溢出还是permanent代溢出。下面一个old代溢出的例子:

2012-07-15T18:51:03.895-0600: [Full GC[PSYoungGen: 279700K->267300K(358400K)]  
[ParOldGen: 685165K->685165K(685170K)]  964865K->964865K(1043570K)  
[PSPermGen: 32390K->32390K(65536K)],0.2499342 secs]  
[Times: user=0.08 sys=0.00, real=0.05 secs]  
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  

 

      上面重要的部分加粗标示了,由于使用的是吞吐量垃圾回收器,old代的统计信息标示为ParOldGen。这行表示了old代的在FullGC的时候占用的空间。从这个结果来看,可以得出的结论是old代的空间太小了,由于FullGC前后old代的被占用的空间和分配的空间基本相等了,因此,JVM报了OutOfMemoryError。相比较,通过PSPermGen这行可以看出permanent代的空间占用是32390K,和他的容量(65536K)比还是有一定的距离。

 

下面的例子展示了由于permanent太少了而导致的OutOfMemoryError发生的例子:

2012-07-15T18:26:37.755-0600: [Full GC  [PSYoungGen: 0K->0K(141632K)]  
  [ParOldGen: 132538K->132538K(350208K)]  32538K->32538K(491840K)  
  [PSPermGen: 65536K->65536K(65536K)],  0.2430136 secs]  
  [Times: user=0.37 sys=0.00, real=0.24 secs]  
  java.lang.OutOfMemoryError: PermGen space  

 

      同上面一样,把关键行标示出来了,通过PSPermGen这行可以看出在FullGC前后,他的空间占用量都和他的容量相同,可以得出的结论是permanent代的空间条小了,这样就导致了OutOfMemoryError。在这个例子里面,old的占用空间(132538K)远比他的容量(350208K)小。

 

 

调节堆大小,让其不会OutOfMemoryError的方法

 

      如果在垃圾回收日志中观察到OutOfMemoryError,尝试把Java堆的大小扩大到物理内存的80%~90%。尤其需要注意的是堆空间导致的OutOfMemoryError以及一定要增加空间。比如说,增加-Xms和-Xmx的值来解决old代的OutOfMemoryError,增加-XX:PermSize和-XX:MaxPermSize来解决permanent代引起的OutOfMemoryError。记住一点Java堆能够使用的容量受限于硬件以及是否使用64位的JVM。在扩大了Java堆的大小之后,再检查垃圾回收日志,直到没有OutOfMemoryError为止。

 

 

注意

如果应用运行在稳定状态下没有OutOfMemoryError就可以进入下一步了,计算活动对象的大小。

 

 

 

计算活动对象的大小

 

   就像前面提到的,活动对象的大小是应用处于稳定运行状态时,长时间存活数据占用的Java堆的空间大小。换句话说,就是应用稳定运行是,在FullGC之后,old代和permanent代的空间大小。

 

活动对象的大小可以通过垃圾回收日志查看,它提供了一些优化信息,如下:

 

  • old代的Java堆空间占用数量。
  • permanent代的Java堆空间占用数量。

为了保证能够准确的评估应用的活动对象大小,最好的做法是多看几次FullGC之后Java堆空间的大小,保证FullGC是发生在应用处于稳定运行的状态。

 

如果应用没有发生FullGC或者发生FullGC的次数很少,在性能测试环境,可以通过Java监控工具来触发FullGC,比如使用VisualVM和JConsole,这些工具在最新的JDK的bin目录下可以找到,VisualVM集成了JConsole,VisualVM或者JConsole上面有一个触发GC的按钮。 

 

 

 

初始化堆大小配置

 

 

下面的图,给出了应用存活的对象的大小。比较明智的做法是多收集几次FullGC信息,有更多的信息,能够做出更加好的决定。



 

 

配置堆大小

 

比较常规是,Java堆大小的初始化值和最大值(通过-Xms和-Xmx选项来指定)应该是old代活动对象的大小的3到4倍。

 

在上图中显示的FullGC信息中,在FullGC之后old代的大小是295111K,差不多是295M,即活动的对象的大小是295M。因此,推荐的Java堆的初始化和最大值应该是885M到1180M,即可以设置为-Xms885m -Xmx1180m。在这个例子中,Java堆的大小是1048570K差不多1048M,在推荐值范围内。

 

 

配置方法区大小

 

permanent的初始值和最大值(-XX:PermSize和-XX:MaxPermSize)应该permanent代活动对象大小的1.2到1.5倍。在上图中看到在FullGC之后permanent代占用空间是32390K,差不多32M。因此,permanent代的推荐大小是38M到48M,即可以设置为-XX:PermSize=48m -XX:MaxPermSize=48m(1.5倍)。这个例子里面,permanent代的空间大小是65536K即64M,大出了17M,不过在1G内存的系统的中,这个数值完全可以忍受。

 

 

配置年轻代大小

 

另外一个常规是,young代空间应该是old代活动对象大小的1到1.5倍。那么在这里例子中,young代的大小可以设置为295M到442M。本例里面,young代的空间大小的358400K,差不多358M,在推荐值中间。

 

如果推荐的Java堆的初始值和最大值是活动对象大小3到4倍,而young代的推荐只是1到1.5倍,那么old代空间大小应该是2到3倍。

 

还有,将对的初始值和最大值设置成一样,会更好

java -Xms1180m -Xmx1180m -Xmn295m

 

 

 

另外一些考虑

 

      本节将提及到在进行应用内存占用评估的时候,另外一些需要记住的点。首先,必须要知道,前面只是评估的Java堆的大小,而不是Java应用占用的所有的内存,如果要查看Java应用占用的所有内存在linux下可以通过top命令查看或者在window下面通过任务管理器来查看,尽管Java堆的大小可能对Java应用占用内存做出了最大的贡献。 比如说,为了存储线程堆栈,应用需要额外的内存,越多的线程,越多内存被线程栈消耗,越深的方法间调用,线程栈越多。另外,本地库需要分配额外的内存,I/O缓存也需要额外的内存。应用的内存消耗需要评估到应用任何一个会消耗内存的地方。

 

      记住,这一步操作不一定能够满足应用内存消耗的需求,如果不能满足,就回过头来看需求是否合理或者修改应用程序。比较可行的一种办法是修改应用程序减小对象的分配,从而减少内存的消耗。

 

 

  • 大小: 72.7 KB
  • 大小: 129.4 KB
分享到:
评论

相关推荐

    测定JVM中对象占用内存—SizeOf

    原项目下载地址:...使用说明: 1、将SizeOf.jar放到Eclipse工程路径下,添加到classpath中; 2、运行前添加VM参数:-javaagent:lib/SizeOf.jar 运行即可(将jar放在lib路径下)。

    jvm调优实战经验

    大鱼塘O(可分配内存): JVM可以调度使用的总的内存数,这个数量受操作系统进程寻址范围、系统虚拟内存总数、系统物理内存总数、其他系统运行所占用的内存资源等因素的制约。 小池塘A(堆内存):JVM运行时数据区域...

    优化Java堆大小的5个技巧

    此文章从5个方面讲解优化java堆: 1.JVM:对难以理解的东西产生恐惧感 2.数据和应用程序为王:回顾静态占用需求 3.业务流量设置规则:审查动态内存占用需求 4.量体裁衣 5.分而治之

    resin-jvm 调优

    gc即垃圾收集机制是指jvm用于释放那些不再使用的对象所占用的内存。java语言并不要求jvm有gc,也没有规定gc如何工作。不过常用的jvm都有gc,而且大多数gc都使用类似的算法管理内存和执行收集操作。 在充分理解了...

    weblogic优化指南.pdf

    垃圾收集(GC)是指JVM释放Java堆中不再使用的对象所占用的内存的过程,而Java堆(Heap)是指Java应用程序对象生存的空间。堆大小决定了GC的频度和时间。堆越大,GC频度低,速度慢。堆越小,GC频度高,速度快。所以GC和堆大小...

    Aqua Data Studio 17.0扩大JVM方法

    Aqua Data Studio默认的JVM比较保守,在实际使用过程中发现往往不够用,可以通过文档中指导的方法配置,扩大JVM,对查询结果很多的情况很有帮助。

    关于Flume的优化和高可用

    文章目录Flume优化一、内存参数优化(减少GC)1)-xmx和-xms设置相同值,避免在 GC 后调整堆大小带来的压力。2)JVM heap(堆内存)设置4G或更高二、channel优化Flume如何保证数据安全(高可用)事务机制Flume解决...

    Tomcat内存溢出的三种情况及解决办法分析

    这种怪事是因为JVM已经被系统分配了大量的内存(比如1.5G),并且它至少要占用可用内存的一半。有人发现,在线程个数很多的情况下,你分配给JVM的内存越多,那么,上述错误发生的可能性就越大。 产生这种现象的...

    使用sigar获取系统信息,内存,磁盘,jvm虚拟机等

    使用sigar获取系统信息,例如内存,cpu,磁盘大小,jvm虚拟机信息,内置jar包已及工具类,工具类写了注释,很详细。

    从 Clojure测量对象内存消耗_Clojure_代码_相关文件_下载

    它允许在运行时检查对象及其所有子字段占用的内存量。 与jamm相比的额外功能: 轻松的运行时加载(您可以在 REPL 中随时开始使用它,无需提供额外的启动参数)。 人类可读的大小输出。 jamm JAR 文件与clj-memory-...

    JVM参数设置详细说明

    指定jvm的最小heap大小,如:-Xms=2g,高并发应用,建议和-Xmx一样,防止因为内存收缩/突然增大带来的性能影响。 c: -Xmn 指定jvm中New Generation的大小,如:-Xmn256m。这个参数很影响性能,如果你的程序需要比较...

    WebSphere参数调优.txt

    堆设置过大,会占用过多的内存,使内存资源耗尽,从而会频繁的进行I/O操作来使用虚拟内存。堆设置过小,会使得对象可分配空间变小,从而会频繁的使用垃圾收集机制来释放内存空间,而每次垃圾收集,都会耗用一定的...

    java是去蜗牛还是源码时代-JVM-:JVM-

    java是去蜗牛还是源码时代 JVM-JVM调优总结 -Xms -Xmx -Xmn -Xss(转) 田间的蜗牛chris ...为jvm运行过程中分配的最大内存,比如-Xms500m,表示jvm进程最多只能够占用500M内存 -Xss 为jvm启动的每个线程分配的内

    MemoryAnalyzer(JDK11)-1.12.0.20210602-win32.win32.x86_64_.zip

    eclipse 的一个插件(MAT 也可以单独使用),它分析大内存的 dump 文件时,可以非常直观的看到各个对象在堆空间中所占用的内存大小、类实例数量、对象引用关系、利用 OQL 对象查询,以及可以很方便的找出对象 ...

    SwayDB:JVM的非阻塞持久性和内存中键值存储引擎

    占用空间小:罐子大小约为7.1 MB。 状态:正在测试和性能优化中。 查看。概述简单数据类型-具有本机Java和Scala集合支持的Map , Set , Queue , SetMap和MultiMap 。 使用任何纯条件更新-无查询语言。 使用...

    Eclipse 启动运行速度调优

    通过指示 JVM 最初应分配给堆的内存数量,可以使 JVM 不必在 IDE 占用较多内存时增加堆大小。 &lt;br&gt;-Xmx96m - 此设置指定 Java 虚拟机应对堆使用的最大内存数量。为此数量设置上限表示 Java 进程消耗的内存数量...

    Java 对象(数组)占多大空间(几个字节) 手把手做实验

    本次实验基于jdk8 64位以及以上版本。本机环境为jdk11 先查看一下jvm启动的默认参数,里面有2个参数值对本次实验会造成影响。 命令行: java -XX:+PrintCommandLineFlags -version 查看jvm默认参数 分别是 -XX:+...

    2023年最新Java面试题

    2023年最新Java面试题 Java Heap Space Java虚拟机创建了太多的对象。...unable to create new native Thread JVM占用了太多内存空间,而在JVM中创建线程还要再操作系统中也创建线程。 解决方案:调整JVM中线程大小。

    强大的中文分词器,有使用案例

    强大的中文分词器,有使用案例,不过占用交大的内存空间,可能需要在eclipse中设置一下jvm的堆大小,防止出现outofmemory错误。

Global site tag (gtag.js) - Google Analytics