JVM垃圾回收分代机制及性能调优

来源:互联网 发布:php跨域上传图片 编辑:程序博客网 时间:2024/05/12 02:28


【CSDN会员专属福利】OpenStack Days China 大会门票,先到先得     【知识库】Swift资源大集合    
 

JVM垃圾回收分代机制及性能调优

 191人阅读 评论(0) 收藏 举报
 分类:
 

目录(?)[+]

0.JVM体系结构简介

JVM Specification中的JVM整体架构

  主要包括两个子系统和两个组件,Class Loader(类装载)子系统,Execution Engine(执行引擎)子系统,Runtime Data Area(运行时数据区)组件,Native Interface(本地接口)组件。

  Class loader 子系统的作用 :根 据给定的全限定名类名(如 java.lang.Object)来装载class文件的内容到 Runtime data area 中的method area(方法区域)。Java 程序员可以extends java.lang.ClassLoader 类来写自己的Class loader。

  Execution engine 子系统的作用 :执 行 classes中的指令。任何 JVM specification实现(JDK)的核心是Execution engine, 换句话说:Sun 的JDK 和IBM的JDK好坏主要取决于他们各自实现的Execution  engine的好坏。每个运行中的线程都有一个 Execution engine的实例。 

  Native interface 组件 :与native libraries 交互,是其它编程语言交互的接口。 

  Runtime data area 组件:这个组件就是 JVM中的内存。

 

Runtime data area 的整体架构图

  Runtime data area 主要包括五个部分:Heap (堆), Method Area(方法区域), Java Stack(java 的栈), Program Counter(程序计数器), Native method stack(本地方法栈)。Heap 和Method Area 是被所有线程的共享使用的;而Java stack, Program counter 和 Native method stack 是以线程为粒度的,每个线程独自拥有。

Heap 

   Java程序在运行时创建的所有类实例或数组都放在同一个堆中。而一个Java虚拟实例中只存在一个堆空间,因此所有线程都将共享这个堆。每一个 java程序独占一个JVM实例,因而每个 java程序都有它自己的堆空间,它们不会彼此干扰。但是同一java程序的多个线程都共享着同一个堆空间,就得考虑多线程访问对象(堆数据)的同步问 题。(这里可能出现的异常 java.lang.OutOfMemoryError: Java heap space) 

Method area 

   在Java 虚拟机中,被装载的 class的信息存储在 Method area的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的 class文件,然后读入这个class文件内容并把它传输到虚拟机中。紧接着虚拟机提取其中的类型信息,并将这些信息存储到方法区。该类型中的类(静 态)变量同样也存储在方法区中。与Heap 一样,method area 是多线程共享的,因此要考虑多线程访问的同步问题。比如,假设同时两个线程都企图访问一个名为 Lava的类,而这个类还没有内装载入虚拟机,那么,这时应该只有一个线程去装载它,而另一个线程则只能等待。 (这里可能出现的异常 java.lang.OutOfMemoryError: PermGen full)

Java stack 

   Java stack 以帧为单位保存线程的运行状态。虚拟机只会直接对 Java stack执行两种操作:以帧为单位的压栈或出栈。每当线程调用一个方法的时候,就对当前状态作为一个帧保存到 java stack 中(压栈);当一个方法调用返回时,从java stack 弹出一个帧(出栈)。栈的大小是有一定的限制,这个可能出现StackOverFlow 问题,例如递归的层数太深。

Program counter  

  每个运行中的Java程序,每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。PC寄存器的内容总是指向下一条将被执行指令的地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。  

Native method stack  

   对于一个运行中的Java程序而言,它还能会用到一些跟本地方法相关的数据区。当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限 制的世界。本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,不止如此,它还可以做任何它想做的事情。比如,可以调用寄存器,或在操作系统中分配 内存等。总之,本地方法具有和JVM 相同的能力和权限。  (这里出现 JVM无法控制的内存溢出问题 native heap OutOfMemory ) 。

Sun JVM 中对 JVM Specification 的实现(内存部分)   

   JVM Specification只是抽象的说明了 JVM 实例按照子系统、内存区、数据类型以及指令这几个术语来描述的,  但是规范并非是要强制规定 Java 虚拟机实现内部的体系结构,更多的是为了严格地定义这些实现的外部特征。  Sun JVM 实现中:Runtime data area(JVM  内存)  五个部分中的 Java Stack , Program Counter, Native method stack 三部分和规范中的描述基本一致;但对 Heap  和  Method Area 进行了自己独特的实现。这个实现和 Sun JVM  的Garbage collector(垃圾回收)机制有关。  

垃圾分代回收算法(Generational Collecting)  

  基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。 

如上图所示,为Java 堆中的各代分布。  

   1. Young(年轻代)JVM specification 中的  Heap的一部分。年轻代分三个区。一个Eden区,两个 Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到 Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个 Survivor区,当这个 Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到年老区(Tenured)。需要注 意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来的对象,和从前一个 Survivor复制过来的对象,而复制到年老区的只有从第一个 Survivor 去过来的对象。而且,Survivor 区总有一个是空的。  

  2. Tenured(年老代)JVM specification中的  Heap的一部分。年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。

   3. Perm(持久代)  JVM specification 中的  Method area 用于存放静态文件,如 Java 类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些 class,例如 Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。


1.内存分代

JVM的内存分代管理结构:

JVM Tunning Practice(1) - 内存分代 - Harry - 染出一道彩虹

下面是一些需要关注的常用的JVM内存配置参数,我们来看看它们是如何影响上图中的比例的。

1)Heap Size

-Xmx ---最大Heap Size,即上图的Total size(包括Eden+form+to,Tenured,不包含Perm,见上图),限制了年轻代和年老代的可分配最大值;

-Xms ---初始化分配的Heap Size

生产环境中ms一般设置成跟mx相等,因为若ms不等于mx那么在某些场景下JVM可能需要对Heap Size进行频繁的扩展和收缩,增加处理时间;

2)New/Young Generation Size

-Xmn ---最大年轻代大小,即上图中的Eden+S0+S1+Virtual

-XX:NewSize ---初始化年轻代大小,即上图中的Eden+S0+S1,在只设置了-Xmn不设置-XX:NewSize的情况下,NewSize等于mn。

生产环境中一般只需设置-Xmn或者设置mn和NewSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;

3)Old Generation Size (Tenured)

-XX:NewRatio --- Old Size/New Size,通过年老代和年轻代的比例和Heap Size就可以算出年老代的大小。一般默认为8,若Heap Size为1024m,则 NewSize=HeapSize/(NewRatio+1)=114m,OldSize=HeapSize-NewSize=910m;

注意:-Xmn的优先级比-XX:NewRatio高,若-Xmn已指定,则OldSize=HeapSize-NewSize,无需再按比例计算。生产环境中一般只需指定-Xmn就足够了。

4)Eden和S0、S1

-XX:SurvivorRatio --- Eden/S0,即 Eden区和S0的比例,默认为8,若NewSize为114m,则S0=NewSize/(SurvivorRatio+2)=11.4m;

S0==S1,S0、S1的职能是一模一样的,又叫做From space和To space,在每一次minor gc后角色会交换。

注意:-XX类型的选项在不同的JDK版本或实现中定义可能有所区别,在近日的实践中发现,

在Linux jdk_1_5_0_10_x86版本中,SurvivorRatio=(YoungSize/S0),而Linux jdk_1_5_0_20_x64版本中,SurvivorRatio=(Eden/S0)

所以,我们在实际的工程实践中还是应该用jmap -heap输出的jvm内存结构信息为准,不要想当然。

5)Permanent Generation Size

-XX:MaxPermSize ---最大持久代大小,默认为64m;

-XX:PermSize ---初始化持久代大小,默认为16m;

生产环境中一般设置MaxPermSize和PermSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;

当应用引用的类比较多或者应用了一些动态类生产技术时应该加大该区的值,一般256m对服务器程序都很足够了。


下面是一些JVM对Native Memory内存的使用:

6)Thread Stack Size

-Xss ---线程堆栈大小,一般用于存放方法入口参数和返回值,以及原子类型的本地变量(即方法内部变量);

一般可设置为128k.

7)Direct Memory Size

-XX:MaxDirectMemorySize ---direct byte buffer用到的本地内存,在本blog的一篇《xsocket内存泄漏》文章中介绍过该参数的作用。默认跟mx相等,所以生产环境中一般不设置mx大于物理内存的一半。

2.GC过程  

在讲述GC过程前我先解释一下JVM的两个控制参数:

-XX:TargetSurvivorRatio --- Survivor Space最大使用率,若存放对象的总大小超过该值,将引起对象向Old区迁移;

-XX:MaxTenuringThreshold --- Young区对象的最大任期阀值,即可经历minor gc的次数,超过该次数的对象直接迁移到Old区;

实际的TenuringThreshold由JVM通过Survivor Space的占用率和TargetSurvivorRatio动态计算,详情请查看参考资料。

《HP-MemoryManagement.pdf》中有对JVM GC过程的形象描述,我借用其中的一些图例来说明一下。

1)Heap在初始状态

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

2)在Eden存放新对象

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

3)Eden空间不足分配新对象,进行第一次minor gc

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

4) Eden区再次被写满,进行第二次minor gc

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

5)Eden再次被写满,进行第3次minor gc

第3次gc,发生了对象从from space提升到old区的迁移,然后也发生了from space到to space的copy

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

以下是Survivor space空间不足但对象的minor gc次数未到达MaxTenuringThreshold时的gc情况:

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

3.GC实战  

在进行GC Tuning时有两个很强大的利器:

jstat:用于查看某java进程的gc情况;

jmap:查看java进程堆栈分配和使用情况,以及dump出当前堆栈内容(可以用Eclipse MAT进行进一步分析)

以上两个利器都是jdk自带,且无需java进程添加任何额外的debug信息输出参数的,直接就可以对任意java进程进行跟踪了。


       我认为GC调优的整体目标是要减缓GC的总体时间增加和降低每次gc引起的应用停顿(大概就是每次执行gc消耗的时间)。但往往我们只能在两者间获取一个平衡,在吞吐量和应用暂停时间之间取得一个平衡。

JVM Tunning Practice(2) - GC Tunning - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC Tunning - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC Tunning - Harry - 染出一道彩虹

调优前配置:-Xmx1024m -Xms1024m

调优前gc情况:jstat -gcutil <pid> 3000

minor gc: 3~6次/3秒

full gc: 1次/30秒


调优后配置:-Xmx2g -Xms2g -Xmn1g -Xss128k -XX:NewSize=1g -XX:PermSize=128m

-XX:MaxPermSize=256m -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=60 

-XX:MaxTenuringThreshold=20 -XX:+UseParNewGC -XX:ParallelGCThreads=8 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0

调优后gc情况:

minor gc: 1次/15秒

full gc: 1次/数小时到数十小时


UseParNewGC表示对新生代采用并行gc;

ParallelGCThreads表示并行的线程数为8,一般是cpu的核个数,当核个数大于8时可能不是很适用;

UseConcMarkSweepGC表示对full gc采用CMS gc;


另外还有几个跟GC有关的有用参数,这里没有用到:

-XX:+DisableExplicitGC 表示禁止显式gc,System.gc()

-XX:+UseCMSCompactAtFullCollection 适用于CMS gc,表示在进行gc的同时清理内存碎片,但会加长gc的总时间

-XX:CMSInitiatingOccupancyFraction=80 适用于CMS gc,表示在年老代达到80%使用率时马上进行回收


另外下面是在JVM Crash时获heap信息的一些配置参数:

-XX:ErrorFile=./xxx.log   JVM Crash时记录heap信息

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./yyy.log JVM OOM时记录heap信息

拿到heap文件后可以用Eclipse MAT进行分析,找出引起内存泄漏的class。


参考资料:

1)HP-MemoryManagement.pdf

2)http://www.51testing.com/?uid-77492-action-viewspace-itemid-203728

3)http://sunqi.javaeye.com/blog/486048

4)http://fallenlord.blogbus.com/logs/57543373.html


0
0
 
 

我的同类文章

  • eclipse 默认UTF-8 -Dfile.encoding=UTF-82016-02-23
  • 2014 年度 Git@OSC 最热门的 50 个项目2016-01-27
  • eclipse 查看class 所属jar包2016-01-20
  • hibernate中对象的状态有三种2016-01-18
  • eclipse 添加jar包几种方式对比2016-01-03
  • 正则表达式总结图2015-12-28
  • java volatile详解(转)2016-02-11
  • JDK中的URLConnection参数详解(转)2016-01-26
  • eclipse 自动注释格式2016-01-19
  • java JPA之EntityManager2016-01-15
  • JAVA正则表达式:Pattern类与Matcher类详解(转)2015-12-29
更多文章
猜你在找
Java之路
HTML 5移动开发从入门到精通
零基础学Java系列从入门到精通
iOS移动开发从入门到精通(Xcode7 & Swift2)
从此不求人:自主研发一套PHP前端开发框架
JVM调优总结六-分代垃圾回收详述2
JVM调优总结六-分代垃圾回收详述2
JVM调优总结五-分代垃圾回收详述1
JVM调优总结六分代垃圾回收详述2
JVM调优总结五-分代垃圾回收详述1
查看评论

  暂无评论

* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStackVPN Spark ERP IE10 Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQueryBI HTML5 Spring Apache .NET API HTML SDK IIS Fedora XML LBS UnitySplashtop UML components Windows Mobile Rails QEMU KDE Cassandra CloudStack FTCcoremail OPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide MaemoCompuware 大数据 aptech Perl Tornado Ruby Hibernate ThinkPHP HBase Pure SolrAngular Cloud Foundry Redis Scala Django Bootstrap
    个人资料
     
    Better_Mee
     
    1 2
    • 访问:1871269次
    • 积分:9968
    • 等级: 
    • 排名:第1077名
    • 原创:261篇
    • 转载:35篇
    • 译文:1篇
    • 评论:111条
    博客专栏
    系统原理分析架构

    文章:6篇

    阅读:464416
    文章分类
  • openstack-开发技术(4)
  • openstack-环境及安装配置(11)
  • linux服务搭建及配置(8)
  • openstack-wsgi-restful(9)
  • openstack-horizon(6)
  • openstack-nova(17)
  • openstack-glance-镜像(6)
  • openstack-消息队列(1)
  • libguestfs(1)
  • openstack-E/F版本开发技术(6)
  • openstack ice版集群技术(2)
  • python学习实践(8)
  • openstack-调度(3)
  • openstack-db-数据库(1)
  • openstack-项目实战(1)
  • 系统原理分析架构(10)
  • kvm-qemu-libvirt(5)
  • ceph(8)
  • 前端架构(2)
  • git使用(5)
  • live_migrate热迁移(3)
  • 项目管理相关(4)
  • 性能测试相关(1)
  • 职业规划(1)
  • 高级编程(1)
  • 数据库Mysql-Nosql(7)
  • openstack部署(大规模)(5)
  • openstack-autosacling(2)
  • 自动化持续集成测试(1)
  • 企业相关(1)
  • 虚拟化v2v/p2v/v2p(7)
  • 网络相关技术(5)
  • 产品设计(1)
  • openstack-neutron(9)
  • juno(3)
  • BIG-DATA(1)
  • openstack-入门介绍(1)
  • centos7(10)
  • rabbitMQ(11)
  • MAC(3)
  • openstack性能优化(3)
  • 高可用HA(12)
  • openstack-heat(1)
  • openstack-版本升级(1)
  • 技术架构相关(3)
  • 混合云基础架构(4)
  • openstack-ceilometer(1)
  • java系列(32)
  • Node.js(1)
  • openstack-region-cell-大规模方案(1)
  • maven(1)
  • openstack-大数据(1)
  • windows相关(1)
  • web相关技术(3)
  • springMVC(4)
  • vmware相关(1)
  • openstack混合集成(1)
  • PASS(1)
  • docker(1)
  • jclouds(4)
  • Functional-Programming(1)
  • 系统架构原理实践相关知识(1)
  • 设计模式(5)
  • activiti(3)
  • 分布式缓存(5)
  • 反向代理&负载均衡(0)
  • 吐槽点滴(2)
  • 搜索引擎(1)
  • spring相关(1)
  • C与C++相关(1)
  • PHP(1)
    文章存档
  • 2016年02月(10)
  • 2016年01月(14)
  • 2015年12月(15)
  • 2015年11月(11)
  • 2015年10月(2)
    展开
    阅读排行
  • 系统原理分析架构-开篇(78254)
  • 系统原理分析架构-五-squid(反代理即web缓存服务器)(77927)
  • 系统原理分析架构-四-squid(简介及正向代理)(77254)
  • 系统原理分析架构-三--代理服务器简介及分类(77044)
  • 系统原理分析架构-一-DNS负载均衡(77042)
  • 系统原理分析架构-二-CDN内容分发网络(76881)
  • CentOS 7.0本地yum源地址及 配置yum地址优先级(64572)
  • Python yield语法 使用实战详解(53130)
  • centos7将lvm的分割成两个卷组(51809)
  • 研究openstack中libguestfs密码不能注入到lvm分区镜像中问题(51783)
    评论排行
  • jira 6.0.3 破解 && 汉化(反编译实例讲解)(14)
  • openstack windows下远程debug调试(pycharm)(9)
  • 研究openstack中libguestfs密码不能注入到lvm分区镜像中问题(8)
  • p2v之 virt-p2v(6)
  • CentOS 7.0本地yum源地址及 配置yum地址优先级(6)
  • nova修改libvirt.xml及自定义xml文件修改nova代码支持vhost(6)
  • openstack nova后端使用ceph rbd(增加在线迁移live_migrate和快照snapshot功能)(5)
  • openstack-wsgi的route中增加api流程详解(os-networks)增加(5)
  • openstack ice版availability zones host aggregates 实战详解(4)
  • 详解修复nova的后端镜像文件挂ceph,生成虚拟机文件变大1024倍(bug)(4)
    推荐文章
    • *Android RocooFix 热修复框架
    • *笑谈Android图表------MPAndroidChart
    • *Nginx正反向代理、负载均衡等功能实现配置
    • * 浅析ZeroMQ工作原理及其特点
    • *Android开源框架Universal-Image-Loader基本介绍及使用
    • *Spring Boot 实践折腾记(三):三板斧,Spring Boot下使用Mybatis
    最新评论
  • openstack nova后端使用ceph rbd(增加在线迁移live_migrate和快照snapshot功能)

    四妮儿L: 楼主你好,请问你实现热迁移的过程,基本的配置只涉及到了libvirtd.conf吗

  • openstack_ice之wsgi详解(paste从ini配置文件->routesr具体发布流程)

    ljl20032021: 写的真好! 崇拜大神

  • openstack nova后端使用ceph rbd(增加在线迁移live_migrate和快照snapshot功能)

    DamonLai: 楼上的回答也是我想问的,ceph确实没实现raw的快照,qcow2是可以迁移的,听说ceph10.2...

  • 系统原理分析架构-开篇

    茂戈: 有见地

  • 实战DDD(Domain-Driven Design领域驱动设计:Evans DDD)

    鱼时代: 说的挺好,有原文连接吗?

  • horizon开发环境搭建及keystone使用总结

    netorgcom: 超级牛鼻~~`

  • 远程debug调试(eclipse)之openstack windows

    qq_34249082: 赞!

  • openstack windows下远程debug调试(pycharm)

    zkp_java: 本机在局域网内,服务器在外网,pycharm没法接受到调试信息啊

  • openstack juno 完全按照官方文档安装

    shenmou98: 您好, 文章开关放的百度云链接失效了。。 能不能再分享一下百度云链接呢?

  • openstack windows开发环境搭建(samba文件共享)

    zkp_java: 请问在mac下pycharm怎么打开samba共享的文件夹啊,要不要像windows那样映射驱动

【CSDN会员专属福利】OpenStack Days China 大会门票,先到先得     【知识库】Swift资源大集合    
 

JVM垃圾回收分代机制及性能调优

 191人阅读 评论(0) 收藏 举报
 分类:
 

目录(?)[+]

0.JVM体系结构简介

JVM Specification中的JVM整体架构

  主要包括两个子系统和两个组件,Class Loader(类装载)子系统,Execution Engine(执行引擎)子系统,Runtime Data Area(运行时数据区)组件,Native Interface(本地接口)组件。

  Class loader 子系统的作用 :根 据给定的全限定名类名(如 java.lang.Object)来装载class文件的内容到 Runtime data area 中的method area(方法区域)。Java 程序员可以extends java.lang.ClassLoader 类来写自己的Class loader。

  Execution engine 子系统的作用 :执 行 classes中的指令。任何 JVM specification实现(JDK)的核心是Execution engine, 换句话说:Sun 的JDK 和IBM的JDK好坏主要取决于他们各自实现的Execution  engine的好坏。每个运行中的线程都有一个 Execution engine的实例。 

  Native interface 组件 :与native libraries 交互,是其它编程语言交互的接口。 

  Runtime data area 组件:这个组件就是 JVM中的内存。

 

Runtime data area 的整体架构图

  Runtime data area 主要包括五个部分:Heap (堆), Method Area(方法区域), Java Stack(java 的栈), Program Counter(程序计数器), Native method stack(本地方法栈)。Heap 和Method Area 是被所有线程的共享使用的;而Java stack, Program counter 和 Native method stack 是以线程为粒度的,每个线程独自拥有。

Heap 

   Java程序在运行时创建的所有类实例或数组都放在同一个堆中。而一个Java虚拟实例中只存在一个堆空间,因此所有线程都将共享这个堆。每一个 java程序独占一个JVM实例,因而每个 java程序都有它自己的堆空间,它们不会彼此干扰。但是同一java程序的多个线程都共享着同一个堆空间,就得考虑多线程访问对象(堆数据)的同步问 题。(这里可能出现的异常 java.lang.OutOfMemoryError: Java heap space) 

Method area 

   在Java 虚拟机中,被装载的 class的信息存储在 Method area的内存中。当虚拟机装载某个类型时,它使用类装载器定位相应的 class文件,然后读入这个class文件内容并把它传输到虚拟机中。紧接着虚拟机提取其中的类型信息,并将这些信息存储到方法区。该类型中的类(静 态)变量同样也存储在方法区中。与Heap 一样,method area 是多线程共享的,因此要考虑多线程访问的同步问题。比如,假设同时两个线程都企图访问一个名为 Lava的类,而这个类还没有内装载入虚拟机,那么,这时应该只有一个线程去装载它,而另一个线程则只能等待。 (这里可能出现的异常 java.lang.OutOfMemoryError: PermGen full)

Java stack 

   Java stack 以帧为单位保存线程的运行状态。虚拟机只会直接对 Java stack执行两种操作:以帧为单位的压栈或出栈。每当线程调用一个方法的时候,就对当前状态作为一个帧保存到 java stack 中(压栈);当一个方法调用返回时,从java stack 弹出一个帧(出栈)。栈的大小是有一定的限制,这个可能出现StackOverFlow 问题,例如递归的层数太深。

Program counter  

  每个运行中的Java程序,每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。PC寄存器的内容总是指向下一条将被执行指令的地址,这里的地址可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。  

Native method stack  

   对于一个运行中的Java程序而言,它还能会用到一些跟本地方法相关的数据区。当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限 制的世界。本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,不止如此,它还可以做任何它想做的事情。比如,可以调用寄存器,或在操作系统中分配 内存等。总之,本地方法具有和JVM 相同的能力和权限。  (这里出现 JVM无法控制的内存溢出问题 native heap OutOfMemory ) 。

Sun JVM 中对 JVM Specification 的实现(内存部分)   

   JVM Specification只是抽象的说明了 JVM 实例按照子系统、内存区、数据类型以及指令这几个术语来描述的,  但是规范并非是要强制规定 Java 虚拟机实现内部的体系结构,更多的是为了严格地定义这些实现的外部特征。  Sun JVM 实现中:Runtime data area(JVM  内存)  五个部分中的 Java Stack , Program Counter, Native method stack 三部分和规范中的描述基本一致;但对 Heap  和  Method Area 进行了自己独特的实现。这个实现和 Sun JVM  的Garbage collector(垃圾回收)机制有关。  

垃圾分代回收算法(Generational Collecting)  

  基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。 

如上图所示,为Java 堆中的各代分布。  

   1. Young(年轻代)JVM specification 中的  Heap的一部分。年轻代分三个区。一个Eden区,两个 Survivor区。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到 Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个 Survivor区,当这个 Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到年老区(Tenured)。需要注 意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来的对象,和从前一个 Survivor复制过来的对象,而复制到年老区的只有从第一个 Survivor 去过来的对象。而且,Survivor 区总有一个是空的。  

  2. Tenured(年老代)JVM specification中的  Heap的一部分。年老代存放从年轻代存活的对象。一般来说年老代存放的都是生命期较长的对象。

   3. Perm(持久代)  JVM specification 中的  Method area 用于存放静态文件,如 Java 类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些 class,例如 Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。


1.内存分代

JVM的内存分代管理结构:

JVM Tunning Practice(1) - 内存分代 - Harry - 染出一道彩虹

下面是一些需要关注的常用的JVM内存配置参数,我们来看看它们是如何影响上图中的比例的。

1)Heap Size

-Xmx ---最大Heap Size,即上图的Total size(包括Eden+form+to,Tenured,不包含Perm,见上图),限制了年轻代和年老代的可分配最大值;

-Xms ---初始化分配的Heap Size

生产环境中ms一般设置成跟mx相等,因为若ms不等于mx那么在某些场景下JVM可能需要对Heap Size进行频繁的扩展和收缩,增加处理时间;

2)New/Young Generation Size

-Xmn ---最大年轻代大小,即上图中的Eden+S0+S1+Virtual

-XX:NewSize ---初始化年轻代大小,即上图中的Eden+S0+S1,在只设置了-Xmn不设置-XX:NewSize的情况下,NewSize等于mn。

生产环境中一般只需设置-Xmn或者设置mn和NewSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;

3)Old Generation Size (Tenured)

-XX:NewRatio --- Old Size/New Size,通过年老代和年轻代的比例和Heap Size就可以算出年老代的大小。一般默认为8,若Heap Size为1024m,则 NewSize=HeapSize/(NewRatio+1)=114m,OldSize=HeapSize-NewSize=910m;

注意:-Xmn的优先级比-XX:NewRatio高,若-Xmn已指定,则OldSize=HeapSize-NewSize,无需再按比例计算。生产环境中一般只需指定-Xmn就足够了。

4)Eden和S0、S1

-XX:SurvivorRatio --- Eden/S0,即 Eden区和S0的比例,默认为8,若NewSize为114m,则S0=NewSize/(SurvivorRatio+2)=11.4m;

S0==S1,S0、S1的职能是一模一样的,又叫做From space和To space,在每一次minor gc后角色会交换。

注意:-XX类型的选项在不同的JDK版本或实现中定义可能有所区别,在近日的实践中发现,

在Linux jdk_1_5_0_10_x86版本中,SurvivorRatio=(YoungSize/S0),而Linux jdk_1_5_0_20_x64版本中,SurvivorRatio=(Eden/S0)

所以,我们在实际的工程实践中还是应该用jmap -heap输出的jvm内存结构信息为准,不要想当然。

5)Permanent Generation Size

-XX:MaxPermSize ---最大持久代大小,默认为64m;

-XX:PermSize ---初始化持久代大小,默认为16m;

生产环境中一般设置MaxPermSize和PermSize相等,理由和HeapSize的设置一样,避免容量震荡消耗资源;

当应用引用的类比较多或者应用了一些动态类生产技术时应该加大该区的值,一般256m对服务器程序都很足够了。


下面是一些JVM对Native Memory内存的使用:

6)Thread Stack Size

-Xss ---线程堆栈大小,一般用于存放方法入口参数和返回值,以及原子类型的本地变量(即方法内部变量);

一般可设置为128k.

7)Direct Memory Size

-XX:MaxDirectMemorySize ---direct byte buffer用到的本地内存,在本blog的一篇《xsocket内存泄漏》文章中介绍过该参数的作用。默认跟mx相等,所以生产环境中一般不设置mx大于物理内存的一半。

2.GC过程  

在讲述GC过程前我先解释一下JVM的两个控制参数:

-XX:TargetSurvivorRatio --- Survivor Space最大使用率,若存放对象的总大小超过该值,将引起对象向Old区迁移;

-XX:MaxTenuringThreshold --- Young区对象的最大任期阀值,即可经历minor gc的次数,超过该次数的对象直接迁移到Old区;

实际的TenuringThreshold由JVM通过Survivor Space的占用率和TargetSurvivorRatio动态计算,详情请查看参考资料。

《HP-MemoryManagement.pdf》中有对JVM GC过程的形象描述,我借用其中的一些图例来说明一下。

1)Heap在初始状态

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

2)在Eden存放新对象

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

3)Eden空间不足分配新对象,进行第一次minor gc

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

4) Eden区再次被写满,进行第二次minor gc

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

5)Eden再次被写满,进行第3次minor gc

第3次gc,发生了对象从from space提升到old区的迁移,然后也发生了from space到to space的copy

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

以下是Survivor space空间不足但对象的minor gc次数未到达MaxTenuringThreshold时的gc情况:

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC过程 - Harry - 染出一道彩虹

3.GC实战  

在进行GC Tuning时有两个很强大的利器:

jstat:用于查看某java进程的gc情况;

jmap:查看java进程堆栈分配和使用情况,以及dump出当前堆栈内容(可以用Eclipse MAT进行进一步分析)

以上两个利器都是jdk自带,且无需java进程添加任何额外的debug信息输出参数的,直接就可以对任意java进程进行跟踪了。


       我认为GC调优的整体目标是要减缓GC的总体时间增加和降低每次gc引起的应用停顿(大概就是每次执行gc消耗的时间)。但往往我们只能在两者间获取一个平衡,在吞吐量和应用暂停时间之间取得一个平衡。

JVM Tunning Practice(2) - GC Tunning - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC Tunning - Harry - 染出一道彩虹

JVM Tunning Practice(2) - GC Tunning - Harry - 染出一道彩虹

调优前配置:-Xmx1024m -Xms1024m

调优前gc情况:jstat -gcutil <pid> 3000

minor gc: 3~6次/3秒

full gc: 1次/30秒


调优后配置:-Xmx2g -Xms2g -Xmn1g -Xss128k -XX:NewSize=1g -XX:PermSize=128m

-XX:MaxPermSize=256m -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=60 

-XX:MaxTenuringThreshold=20 -XX:+UseParNewGC -XX:ParallelGCThreads=8 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0

调优后gc情况:

minor gc: 1次/15秒

full gc: 1次/数小时到数十小时


UseParNewGC表示对新生代采用并行gc;

ParallelGCThreads表示并行的线程数为8,一般是cpu的核个数,当核个数大于8时可能不是很适用;

UseConcMarkSweepGC表示对full gc采用CMS gc;


另外还有几个跟GC有关的有用参数,这里没有用到:

-XX:+DisableExplicitGC 表示禁止显式gc,System.gc()

-XX:+UseCMSCompactAtFullCollection 适用于CMS gc,表示在进行gc的同时清理内存碎片,但会加长gc的总时间

-XX:CMSInitiatingOccupancyFraction=80 适用于CMS gc,表示在年老代达到80%使用率时马上进行回收


另外下面是在JVM Crash时获heap信息的一些配置参数:

-XX:ErrorFile=./xxx.log   JVM Crash时记录heap信息

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./yyy.log JVM OOM时记录heap信息

拿到heap文件后可以用Eclipse MAT进行分析,找出引起内存泄漏的class。


参考资料:

1)HP-MemoryManagement.pdf

2)http://www.51testing.com/?uid-77492-action-viewspace-itemid-203728

3)http://sunqi.javaeye.com/blog/486048

4)http://fallenlord.blogbus.com/logs/57543373.html


0
0
 
 

我的同类文章

  • eclipse 默认UTF-8 -Dfile.encoding=UTF-82016-02-23
  • 2014 年度 Git@OSC 最热门的 50 个项目2016-01-27
  • eclipse 查看class 所属jar包2016-01-20
  • hibernate中对象的状态有三种2016-01-18
  • eclipse 添加jar包几种方式对比2016-01-03
  • 正则表达式总结图2015-12-28
  • java volatile详解(转)2016-02-11
  • JDK中的URLConnection参数详解(转)2016-01-26
  • eclipse 自动注释格式2016-01-19
  • java JPA之EntityManager2016-01-15
  • JAVA正则表达式:Pattern类与Matcher类详解(转)2015-12-29
更多文章
猜你在找
Java之路
HTML 5移动开发从入门到精通
零基础学Java系列从入门到精通
iOS移动开发从入门到精通(Xcode7 & Swift2)
从此不求人:自主研发一套PHP前端开发框架
JVM调优总结六-分代垃圾回收详述2
JVM调优总结六-分代垃圾回收详述2
JVM调优总结五-分代垃圾回收详述1
JVM调优总结六分代垃圾回收详述2
JVM调优总结五-分代垃圾回收详述1
查看评论

  暂无评论

* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题 Hadoop AWS 移动游戏 Java Android iOS Swift 智能硬件 Docker OpenStackVPN Spark ERP IE10 Eclipse CRM JavaScript 数据库 Ubuntu NFC WAP jQueryBI HTML5 Spring Apache .NET API HTML SDK IIS Fedora XML LBS UnitySplashtop UML components Windows Mobile Rails QEMU KDE Cassandra CloudStack FTCcoremail OPhone CouchBase 云计算 iOS6 Rackspace Web App SpringSide MaemoCompuware 大数据 aptech Perl Tornado Ruby Hibernate ThinkPHP HBase Pure SolrAngular Cloud Foundry Redis Scala Django Bootstrap
    个人资料
     
    Better_Mee
     
    1 2
    • 访问:1871269次
    • 积分:9968
    • 等级: 
    • 排名:第1077名
    • 原创:261篇
    • 转载:35篇
    • 译文:1篇
    • 评论:111条
    博客专栏
    系统原理分析架构

    文章:6篇

    阅读:464416
    文章分类
  • openstack-开发技术(4)
  • openstack-环境及安装配置(11)
  • linux服务搭建及配置(8)
  • openstack-wsgi-restful(9)
  • openstack-horizon(6)
  • openstack-nova(17)
  • openstack-glance-镜像(6)
  • openstack-消息队列(1)
  • libguestfs(1)
  • openstack-E/F版本开发技术(6)
  • openstack ice版集群技术(2)
  • python学习实践(8)
  • openstack-调度(3)
  • openstack-db-数据库(1)
  • openstack-项目实战(1)
  • 系统原理分析架构(10)
  • kvm-qemu-libvirt(5)
  • ceph(8)
  • 前端架构(2)
  • git使用(5)
  • live_migrate热迁移(3)
  • 项目管理相关(4)
  • 性能测试相关(1)
  • 职业规划(1)
  • 高级编程(1)
  • 数据库Mysql-Nosql(7)
  • openstack部署(大规模)(5)
  • openstack-autosacling(2)
  • 自动化持续集成测试(1)
  • 企业相关(1)
  • 虚拟化v2v/p2v/v2p(7)
  • 网络相关技术(5)
  • 产品设计(1)
  • openstack-neutron(9)
  • juno(3)
  • BIG-DATA(1)
  • openstack-入门介绍(1)
  • centos7(10)
  • rabbitMQ(11)
  • MAC(3)
  • openstack性能优化(3)
  • 高可用HA(12)
  • openstack-heat(1)
  • openstack-版本升级(1)
  • 技术架构相关(3)
  • 混合云基础架构(4)
  • openstack-ceilometer(1)
  • java系列(32)
  • Node.js(1)
  • openstack-region-cell-大规模方案(1)
  • maven(1)
  • openstack-大数据(1)
  • windows相关(1)
  • web相关技术(3)
  • springMVC(4)
  • vmware相关(1)
  • openstack混合集成(1)
  • PASS(1)
  • docker(1)
  • jclouds(4)
  • Functional-Programming(1)
  • 系统架构原理实践相关知识(1)
  • 设计模式(5)
  • activiti(3)
  • 分布式缓存(5)
  • 反向代理&负载均衡(0)
  • 吐槽点滴(2)
  • 搜索引擎(1)
  • spring相关(1)
  • C与C++相关(1)
  • PHP(1)
    文章存档
  • 2016年02月(10)
  • 2016年01月(14)
  • 2015年12月(15)
  • 2015年11月(11)
  • 2015年10月(2)
    展开
    阅读排行
  • 系统原理分析架构-开篇(78254)
  • 系统原理分析架构-五-squid(反代理即web缓存服务器)(77927)
  • 系统原理分析架构-四-squid(简介及正向代理)(77254)
  • 系统原理分析架构-三--代理服务器简介及分类(77044)
  • 系统原理分析架构-一-DNS负载均衡(77042)
  • 系统原理分析架构-二-CDN内容分发网络(76881)
  • CentOS 7.0本地yum源地址及 配置yum地址优先级(64572)
  • Python yield语法 使用实战详解(53130)
  • centos7将lvm的分割成两个卷组(51809)
  • 研究openstack中libguestfs密码不能注入到lvm分区镜像中问题(51783)
    评论排行
  • jira 6.0.3 破解 && 汉化(反编译实例讲解)(14)
  • openstack windows下远程debug调试(pycharm)(9)
  • 研究openstack中libguestfs密码不能注入到lvm分区镜像中问题(8)
  • p2v之 virt-p2v(6)
  • CentOS 7.0本地yum源地址及 配置yum地址优先级(6)
  • nova修改libvirt.xml及自定义xml文件修改nova代码支持vhost(6)
  • openstack nova后端使用ceph rbd(增加在线迁移live_migrate和快照snapshot功能)(5)
  • openstack-wsgi的route中增加api流程详解(os-networks)增加(5)
  • openstack ice版availability zones host aggregates 实战详解(4)
  • 详解修复nova的后端镜像文件挂ceph,生成虚拟机文件变大1024倍(bug)(4)
    推荐文章
    • *Android RocooFix 热修复框架
    • *笑谈Android图表------MPAndroidChart
    • *Nginx正反向代理、负载均衡等功能实现配置
    • * 浅析ZeroMQ工作原理及其特点
    • *Android开源框架Universal-Image-Loader基本介绍及使用
    • *Spring Boot 实践折腾记(三):三板斧,Spring Boot下使用Mybatis
    最新评论
  • openstack nova后端使用ceph rbd(增加在线迁移live_migrate和快照snapshot功能)

    四妮儿L: 楼主你好,请问你实现热迁移的过程,基本的配置只涉及到了libvirtd.conf吗

  • openstack_ice之wsgi详解(paste从ini配置文件->routesr具体发布流程)

    ljl20032021: 写的真好! 崇拜大神

  • openstack nova后端使用ceph rbd(增加在线迁移live_migrate和快照snapshot功能)

    DamonLai: 楼上的回答也是我想问的,ceph确实没实现raw的快照,qcow2是可以迁移的,听说ceph10.2...

  • 系统原理分析架构-开篇

    茂戈: 有见地

  • 实战DDD(Domain-Driven Design领域驱动设计:Evans DDD)

    鱼时代: 说的挺好,有原文连接吗?

  • horizon开发环境搭建及keystone使用总结

    netorgcom: 超级牛鼻~~`

  • 远程debug调试(eclipse)之openstack windows

    qq_34249082: 赞!

  • openstack windows下远程debug调试(pycharm)

    zkp_java: 本机在局域网内,服务器在外网,pycharm没法接受到调试信息啊

  • openstack juno 完全按照官方文档安装

    shenmou98: 您好, 文章开关放的百度云链接失效了。。 能不能再分享一下百度云链接呢?

  • openstack windows开发环境搭建(samba文件共享)

    zkp_java: 请问在mac下pycharm怎么打开samba共享的文件夹啊,要不要像windows那样映射驱动

0 0
原创粉丝点击