★JVM 的内存空间

  在 Java 虚拟机规范中,提及了如下几种类型的内存空间:

◇栈内存(Stack):每个线程私有的。
◇堆内存(Heap):所有线程公用的。
◇方法区(Method Area):有点像以前常说的“进程代码段”,这里面存放了每个加载类的反射信息、类函数的代码、编译时常量等信息。
◇原生方法栈(Native Method Stack):主要用于 JNI 中的原生代码,平时很少涉及。

★垃圾回收机制简介

  其实 Java 虚拟机规范中并未规定垃圾回收的相关细节。垃圾回收具体该怎么搞,完全取决于各个 JVM 的设计者。所以,不同的 JVM 之间,GC 的行为可能会有一定的差异。下面咱拿 SUN 官方的 JVM 来简单介绍一下 GC 的机制。

◇啥时候进行垃圾回收?

  一般情况下,当 JVM 发现堆内存比较紧张、不太够用时,它就会着手进行垃圾回收工作。但是大伙儿要认清这样一个残酷的事实:JVM 进行 GC 的时间点是无法准确预知的。因为 GC 启动的时刻会受到各种运行环境因素的影响,随机性太大。
  虽说咱们无法准确预知,但如果你想知道每次垃圾回收执行的情况,还是蛮方便的。可以通过 JVM 的命令行参数“-XX:+PrintGC”把相关信息打印出来。
  另外,调用 System.gc() 只是建议 JVM 进行 GC。至于 JVM 到底会不会真的去做,只有天晓得。所以,通常不建议自己手动调用 System.gc(),还是让 JVM 自行决定比较好。另外,使用 JVM 命令行参数“-XX:+DisableExplicitGC”可以让 System.gc() 不起作用。

◇谁来负责垃圾回收?

  一般情况下,JVM 会有一个或多个专门的垃圾回收线程,由它们负责清理回收垃圾内存。

◇如何发现垃圾对象?

  垃圾回收线程会从“根集(Root Set)”开始进行对象引用的遍历。所谓的“根集”,就是正在运行的线程中,可以访问的【引用变量】的集合(比如所有线程当前函数的参数和局部变量、当前类的成员变量等等)。垃圾回收线程先找出被根集直接引用的所有对象(不妨叫集合1),然后再找出被集合1直接引用的所有对象(不妨叫集合2),然后再找出被集合2直接引用的所有对象......如此循环往复,直到把能遍历到的对象都遍历完。
  凡是从“根集”通过上述遍历可以到达的对象,都称为可达对象或有效对象;反之,则是不可达对象或失效对象(也就是垃圾)。

◇如何清理/回收垃圾?

  通过上述阶段,就把垃圾对象都找出来。然后垃圾回收线程会进行相应的清理和回收工作,包括:把垃圾内存重新变为可用内存、进行内存的整理以消除内存碎片、等等。这个过程会涉及到若干算法,限于篇幅,咱就不深入聊了。

◇分代

  早期的 JVM 是不采用分代技术的,所有被 GC 管理的对象都存放在同一个堆里面。这么做的缺点比较明显:每次进行GC都要遍历所有对象,开销很大。其实大部分的对象生命周期都很短(短命对象),只有少数对象比较长寿;在这些短命对象中,又只有少数对象占用的内存空间大;其它大量的短命对象都属于小对象(很符合二八原理)。
  有鉴于此,从 JDK 1.2 之后,JVM 开始使用分代的垃圾回收(Generational Garbage Collection)。JVM 把 GC 相关的内存分为“年老代”(Tenured)和“年轻代”(Nursery)、“持久代”(Permanent,对应于 JVM 规范的“方法区”)。【大部分】对象在刚创建时,都位于“年轻代”。如果某对象经历了几轮 GC 还活着(大龄对象),就把它移到“年老代”。另外,如果某个对象在创建时比较大,可能就直接被丢到年老代。经过这种策略,使得年轻代总是保存那些短命的小对象。在空间尺寸上,“年轻代”相对较小,而“年老代”相对较大。
  因为有了分代技术,JVM 的 GC 也相应分为两种——主要收集(Major Collection)和次要收集(Minor Collection)。“主要收集”同时清理年老代和年轻代,因此开销很大,不常进行;“次要收集”仅仅清理年轻代,开销很小,经常进行。

★GC对性能会有啥影响?

  刚才介绍了GC的大致原理,那GC对性能会造成哪些影响捏?主要有如下几个方面:

◇造成当前运行线程的停顿

  早期的 GC 比较弱智。在它工作期间,所有其它的线程都被暂停(以免影响垃圾回收工作)。等到 GC 干完活,其它线程再继续运行。所以,早期 JDK 的 GC 一旦开始工作,整个程序就会陷入假死状态,失去各种响应。
  经过这些年的技术改进(包括采用分代技术),从 JDK 1.4 开始,GC 已经比较精明了。在它干活期间,只是偶尔暂停一下其它线程的运行(从长时间假死变为暂时性休克)。

◇遍历对象引用的开销

  试想如果JVM中的对象很多,那遍历完所有可达对象肯定是比较费劲的工作,这个开销可不小。

◇清理和回收垃圾的开销

  遍历完对象引用之后,对垃圾的清理和回收也有较大的开销。这部分开销可能包括复制内存块、更新对象引用等等。

★几种收集器

◇两个性能指标

  因为今天聊的是性能的话题,必然会提到衡量 GC 性能的两个重要指标:吞吐量(Throughput)和停顿时间(Pause Time)。吞吐量这个词不是很直观,解释一下:就是 JVM【不用于】GC 的时间占总时间的比率。“吞吐量”是越大越好,“停顿时间”是越小越好。
  不同的应用程序对这两个指标的关注点不一样(后面具体会说),也就是所谓的“众口难调”。很多 JVM 厂商为了迎合“众口”,不得不提供多种几种垃圾收集器供使用者选择。不同的收集器,采用的收集策略是不一样的,下面具体介绍。

◇串行收集器(Serial Collector)

  使用命令行选项“-XX:+UseSerialGC”指定。
  这种收集器是最传统的收集器。它使用单线程进行垃圾回收,对于“单 CPU 机器”比较合适。另外,小型应用或者对上述两个指标没有特殊要求的,可以使用串行收集器。

◇并行收集器(Parallel Throughput Collector)

  顾名思义,这种收集器使用多个线程进行垃圾回收以达到高吞吐量。垃圾回收线程的数量通过命令行选项“-XX:ParallelGCThreads=n”指定。可以设置该数值以便充分利用“多CPU 或 多核”。
  当使用命令行选项“-XX:+UseParallelGC”时:它会针对年轻代使用多个垃圾回收线程,对年老代依然使用单个线程的串行方式。此选项最早在JDK 1.5引入。
  当使用命令行选项“-XX:+UseParallelOldGC”时:它针对年轻代和年老代都使用多个垃圾回收线程的方式。不过此选项从 JDK 1.6 才开始引入。

◇并发收集器(Concurrent Low Pause Collector)

  使用命令行选项“-XX:+UseConcMarkSweepGC”指定。
  这种收集器优先保证程序的响应。它会尽量让垃圾回收线程和应用自身的线程同时运行,从而降低停顿时间。此选项从JDK 1.4.1开始支持。

◇增量收集器(Incremental Collector)

  自从 JDK 1.4.2 以来,SUN 官方就停止维护该收集器了。所以俺就节省点口水,不多说了。

★如何降低GC的影响?

◇尽量减少堆内存的使用

  由于 GC 是针对存储在堆内存的对象进行的。咱们如果在程序中减少引用对象的分配(也就相应降低堆内存分配),那对于提高 GC 的性能是很有帮助滴。上次“字符串过滤实战”的帖子给出了一个例子,示范了如何通过降低堆内存的分配次数来提升性能。

◇设置合适的堆内存大小

  JVM 的堆内存是有讲究的,不能太大也不能太小。如果堆内存太小,JVM 老是感觉内存不够用,可能会导致频繁进行垃圾回收,影响了性能;如果堆内存太大,以至于操作系统的大部分物理内存都被 JVM 自个儿霸占了,那可能会影响其它应用程序甚至操作系统本身的性能。
  另外,年轻代的大小(或者说“年轻代”与“年老代”的比值)对于 GC 的性能也有明显影响。如果年轻代太小,可能导致次要收集很频繁;如果年轻代太大,导致次要收集的停顿很明显。
  JVM 提供了若干和堆内存大小相关的命令行选项,具体如下:
------------------------------
-Xms  设置初始堆内存
-Xmx  设置最大堆内存
-Xmn  设置年轻代的大小
-XX:NewRatio=n  设置年轻代与年老代的比例为“n”
-XX:NewSize=n  设置年轻代大小为“n”
------------------------------
  一般情况下,JVM 的默认参数值已经够用。所以没事儿别轻易动用上述选项。如果你非调整不可,一定要做深入的性能对比测试,保证调整后的性能确实优于默认参数值。

◇吞吐量和停顿的取舍

  前面提到了不同应用的众口难调。常见的口味有两种:(1)看重吞吐量,对停顿时间无所谓;(2)侧重于停顿时间。
  对于某些在后台的、单纯运算密集型的应用,属于第一种。比如某些科学计算的应用。这时候建议使用并行收集器。
  对于涉及用户 UI 交互的、实时性要求比较高、程序需要快速响应的,属于第二种。比如某些桌面游戏、某些电信交换系统。这时候建议使用并发收集器。

AJPFX浅谈Java 性能优化之垃圾回收(GC)的更多相关文章

  1. AJPFX浅谈Java性能优化之finalize 函数

    ★finalize 函数的调用机制 俺经常啰嗦:“了解本质机制的重要性”.所以今天也得先谈谈 finalize 函数的调用机制.在聊之前,先声明一下:Java虚拟机规范,并没有硬性规定垃圾回收该不该搞 ...

  2. AJPFX浅谈Java 性能优化之字符串过滤实战

    ★一个简单的需求 首先描述一下需求:给定一个 String 对象,过滤掉除了数字(字符'0'到'9')以外的其它字符.要求时间开销尽可能小.过滤函数的原型如下: String filter(Strin ...

  3. 浅谈java性能分析

    浅谈java性能分析,效能分析 在老师强烈的要求下做了效能分析,对上次写过的词频统计的程序进行分析以及改进. 对于效能分析:我个人很浅显的认为就是程序的运行效率,代码的执行效率等等. java做性能测 ...

  4. 浅谈Oracle 性能优化

    基于大型Oracle数据库应用开发已有6个年头了,经历了从最初零数据演变到目前上亿级的数据存储.在这个经历中,遇到各种各样的性能问题及各种性能优化. 在这里主要给大家分享一下数据库性能优化的一些方法和 ...

  5. 开发高性能的MongoDB应用—浅谈MongoDB性能优化(转)

    出处:http://www.cnblogs.com/mokafamily/p/4102829.html 性能与用户量 “如何能让软件拥有更高的性能?”,我想这是一个大部分开发者都思考过的问题.性能往往 ...

  6. 开发高性能的MongoDB应用—浅谈MongoDB性能优化

    关联文章索引: 大数据时代的数据存储,非关系型数据库MongoDB 性能与用户量 “如何能让软件拥有更高的性能?”,我想这是一个大部分开发者都思考过的问题.性能往往决定了一个软件的质量,如果你开发的是 ...

  7. 浅谈前端性能优化(二)——对HTTP传输进行压缩

    1.前端性能优化的一点: 对js.css.图片等进行压缩,尽可能减小文件的大小,减少文件下载的时间,从而减少网页响应的时间. 2.前端性能优化的另一点: 对HTTP传输进行压缩,即在js,css.图片 ...

  8. 浅谈前端性能优化(PC版)

    前端的性能优化是一个很宽泛的概念,最终目的都是为了提升用户体验,改善页面性能.面试的时候经常会遇到问谈谈性能优化的手段,这个我分几大部分来概述,具体细节需要自己再针对性的去搜索,只是提供一个索引(太多 ...

  9. AJPFX浅谈Java新手问题之缺少良好的编程习惯

    ★随意地命名 有些新手写程序,当需要定义某个变量名(也可能是函数名.类名.包名等)时,随意地一敲键盘,名字就起好了......若干星期后,碰到某 bug,再来看自己写的代码时,心中暗自嘀咕:“这代码是 ...

随机推荐

  1. mybatis中GenericTokenParser的用法

    代码示例 import org.apache.ibatis.parsing.GenericTokenParser;import org.apache.ibatis.parsing.TokenHandl ...

  2. mmwave

    毫米波(mmWave) 致力于支持5G应用创新开发,集成在BEEcube BEE7基带平台上的赛灵思256QAM毫米波调制解调器IP为宽带回程原型设计提供完整的开箱即用型解决方案 赛灵思公司 (NAS ...

  3. SDUT OJ 2892 A (字典树问题-输出出现次数最多的字符串的出现次数,60ms卡时间,指针+最后运行完释放内存)

    A Time Limit: 60ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 给出n(1<= n && n <= 2*10^6)个字 ...

  4. eclipse软件启动弹窗端口问题解决

    如果启动eclipse,弹出一个窗口,上面显示,8080 .8009.……等的提示,说明端口有冲突, 解决办法如下: 1.打开cmd 2.输入 netstat   -ano|findstr  8080 ...

  5. 软件GUI测试中的关注点

    [摘要] 本文列数了软件黑盒测试过程中,在被测试软件中可能存在的常见软件问题.本文不会详细讨论基本的软件测试思想与常用技术,仅针对在软件黑盒测试过程中若干的问题做描述,并提供个人的参考测试意见与防范意 ...

  6. Python安装pip3常见问题

    安装pip3 1.安装 zlib组件: 安装完成后,执行命令 python3 -m pip install redis,报错: RuntimeError: Compression requires t ...

  7. damon

    不管是否有-f参数,最终程序都会进入 fuse_loop_mt 循环中,在helper.c的fuse_main_common函数中. 1. 有-f参数.这种情况下fuse_setup_common函数 ...

  8. How to run Media SDK samples on Skylake【转载】

    In the last few days, we have seen lot of concern for using Intel® Media 2016 on 6th generation Inte ...

  9. [yii2]Module的Namespace和控制器位置

    namespace和目录对应,否则无法找到控制器类,module文件在根路径 使用gii生成Module为\app\admin,那么 namespace app; class admin extend ...

  10. html语义化 -------<fieldset>和<legend>

    为什么HTML代码要语义化,除了代码可读性好以外,SEO有帮助外,最主要的还是对一些屏幕阅读设备或者其他辅助阅读设备友好, 可以让用户在条件受限的条件下依然可以正常使用我们的产品,比方说鼠标坏了,又或 ...