- 浏览: 40427 次
- 性别:
- 来自: 北京
最新评论
开这帖的目的是想让大家了解到,所谓“标准参数”是件很微妙的事情。确实有许多前辈经过多年开发积累下了许多有用的调优经验,但向他们问“标准参数”并照单全收是件危险的事情。
前辈们提供的“标准参数”或许适用于他们的应用场景,他们或许也知道这些参数里隐含的陷阱;但听众却不一定知道各种参数背后的缘由。
原则上说,在生产环境使用非标准参数(这里指的是在各JDK/JRE实现特有的、相互之间不通用的参数)应该尽量避免。这些参数与具体实现密切相关,不是光了解很抽象的“JVM原理”就足以理解的;即便在同一系列的JDK/JRE实现中,非标准参数也不保证在各版本间有一样的作用;而且许多人只看名字就猜想参数的左右,做“调优”却适得其反。
非标准参数的默认值在不同版本间或许会悄然发生变化。这些变化的背后多半有合理的理由。设了一大堆非标准参数、不明就里的同学在升级JDK/JRE的时候也容易掉坑里。
下面用Oracle/Sun JDK 6来举几个例子。
======================================================================
1、-XX:+DisableExplicitGC 与 NIO的direct memory
很多人都见过JVM调优建议里使用这个参数,对吧?但是为什么要用它,什么时候应该用而什么时候用了会掉坑里呢?
首先要了解的是这个参数的作用。在Oracle/Sun JDK这个具体实现上,System.gc()的默认效果是引发一次stop-the-world的full GC,对整个GC堆做收集。有几个参数可以改变默认行为,之前发过一帖简单描述过,这里就不重复了。关键点是,用了-XX:+DisableExplicitGC参数后,System.gc()的调用就会变成一个空调用,完全不会触发任何GC(但是“函数调用”本身的开销还是存在的哦~)。
为啥要用这个参数呢?最主要的原因是为了防止某些手贱的同学在代码里到处写System.gc()的调用而干扰了程序的正常运行吧。有些应用程序本来可能正常跑一天也不会出一次full GC,但就是因为有人在代码里调用了System.gc()而不得不间歇性被暂停。也有些时候这些调用是在某些库或框架里写的,改不了它们的代码但又不想被这些调用干扰也会用这参数。
OK。看起来这参数应该总是开着嘛。有啥坑呢?
其中一种情况是下述三个条件同时满足时会发生的:
1、应用本身在GC堆内的对象行为良好,正常情况下很久都不发生full GC;
2、应用大量使用了NIO的direct memory,经常、反复的申请DirectByteBuffer
3、使用了-XX:+DisableExplicitGC
能观察到的现象是:
Log代码
1.java.lang.OutOfMemoryError: Direct buffer memory
2. at java.nio.Bits.reserveMemory(Bits.java:633)
3. at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:98)
4. at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)
5....
做个简单的例子来演示这现象:
Java代码
1.import java.nio.*;
2.
3.public class DisableExplicitGCDemo {
4. public static void main(String[] args) {
5. for (int i = 0; i < 100000; i++) {
6. ByteBuffer.allocateDirect(128);
7. }
8. System.out.println("Done");
9. }
10.}
然后编译、运行之:
Command prompt代码
1.$ java -version
2.java version "1.6.0_25"
3.Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
4.Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)
5.$ javac DisableExplicitGCDemo.java
6.$ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC -XX:+DisableExplicitGC DisableExplicitGCDemo
7.Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
8. at java.nio.Bits.reserveMemory(Bits.java:633)
9. at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:98)
10. at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)
11. at DisableExplicitGCDemo.main(DisableExplicitGCDemo.java:6)
12.$ java -XX:MaxDirectMemorySize=10m -XX:+PrintGC DisableExplicitGCDemo
13.[GC 10996K->10480K(120704K), 0.0433980 secs]
14.[Full GC 10480K->10415K(120704K), 0.0359420 secs]
15.Done
可以看到,同样的程序,不带-XX:+DisableExplicitGC时能正常完成运行,而带上这个参数后却出现了OOM。
例子里用-XX:MaxDirectMemorySize=10m限制了DirectByteBuffer能分配的空间的限额,以便问题更容易展现出来。不用这个参数就得多跑一会儿了。
在这个例子里,main()里的循环不断申请DirectByteBuffer但并没有引用、使用它们,所以这些DirectByteBuffer应该刚创建出来就已经满足被GC的条件,等下次GC运行的时候就应该可以被回收。
实际上却没这么简单。DirectByteBuffer是种典型的“冰山”对象,也就是说它的Java对象虽然很小很无辜,但它背后却会关联着一定量的native memory资源,而这些资源并不在GC的控制之下,需要自己注意控制好。对JVM如何使用native memory不熟悉的同学可以参考去年JavaOne上IBM的一个演讲,“Where Does All the Native Memory Go”。
Oracle/Sun JDK的实现里,DirectByteBuffer有几处值得注意的地方。
1、DirectByteBuffer没有finalizer,它的native memory的清理工作是通过sun.misc.Cleaner自动完成的。
2、sun.misc.Cleaner是一种基于PhantomReference的清理工具,比普通的finalizer轻量些。对PhantomReference不熟悉的同学请参考Bob Lee最近几年在JavaOne上做的演讲,"The Ghost in the Virtual Machine: A Reference to References"。今年的JavaOne上他也讲了同一个主题,内容比前几年的稍微更新了些。PPT可以从链接里的页面下载到。
Java代码
1./**
2. * General-purpose phantom-reference-based cleaners.
3. *
4. * <p> Cleaners are a lightweight and more robust alternative to finalization.
5. * They are lightweight because they are not created by the VM and thus do not
6. * require a JNI upcall to be created, and because their cleanup code is
7. * invoked directly by the reference-handler thread rather than by the
8. * finalizer thread. They are more robust because they use phantom references,
9. * the weakest type of reference object, thereby avoiding the nasty ordering
10. * problems inherent to finalization.
11. *
12. * <p> A cleaner tracks a referent object and encapsulates a thunk of arbitrary
13. * cleanup code. Some time after the GC detects that a cleaner's referent has
14. * become phantom-reachable, the reference-handler thread will run the cleaner.
15. * Cleaners may also be invoked directly; they are thread safe and ensure that
16. * they run their thunks at most once.
17. *
18. * <p> Cleaners are not a replacement for finalization. They should be used
19. * only when the cleanup code is extremely simple and straightforward.
20. * Nontrivial cleaners are inadvisable since they risk blocking the
21. * reference-handler thread and delaying further cleanup and finalization.
22. *
23. *
24. * @author Mark Reinhold
25. * @version %I%, %E%
26. */
重点是这两句:"A cleaner tracks a referent object and encapsulates a thunk of arbitrary cleanup code. Some time after the GC detects that a cleaner's referent has become phantom-reachable, the reference-handler thread will run the cleaner."
Oracle/Sun JDK 6中的HotSpot VM只会在年老代GC(full GC/major GC或者concurrent GC都算)的时候才会做reference processing,而在young GC/minor GC时不做。
也就是说,做full GC的话会做reference processing,进而能触发Cleaner对已死的DirectByteBuffer对象做清理工作。而如果很长一段时间里没做过GC或者只做了young GC的话则不会触发Cleaner的工作,那么就可能让本来已经死了的DirectByteBuffer关联的native memory得不到及时释放。
3、为DirectByteBuffer分配空间过程中会显式调用System.gc(),以期通过full GC来强迫已经无用的DirectByteBuffer对象释放掉它们关联的native memory:
Java代码
1.// These methods should be called whenever direct memory is allocated or
2.// freed. They allow the user to control the amount of direct memory
3.// which a process may access. All sizes are specified in bytes.
4.static void reserveMemory(long size) {
5.
6. synchronized (Bits.class) {
7. if (!memoryLimitSet && VM.isBooted()) {
8. maxMemory = VM.maxDirectMemory();
9. memoryLimitSet = true;
10. }
11. if (size <= maxMemory - reservedMemory) {
12. reservedMemory += size;
13. return;
14. }
15. }
16.
17. System.gc();
18. try {
19. Thread.sleep(100);
20. } catch (InterruptedException x) {
21. // Restore interrupt status
22. Thread.currentThread().interrupt();
23. }
24. synchronized (Bits.class) {
25. if (reservedMemory + size > maxMemory)
26. throw new OutOfMemoryError("Direct buffer memory");
27. reservedMemory += size;
28. }
29.
30.}
这几个实现特征使得Oracle/Sun JDK 6依赖于System.gc()触发GC来保证DirectByteMemory的清理工作能及时完成。如果打开了-XX:+DisableExplicitGC,清理工作就可能得不到及时完成,于是就有机会见到direct memory的OOM,也就是上面的例子演示的情况。我们这边在实际生产环境中确实遇到过这样的问题。
教训是:如果你在使用Oracle/Sun JDK 6,应用里有任何地方用了direct memory,那么使用-XX:+DisableExplicitGC要小心。如果用了该参数而且遇到direct memory的OOM,可以尝试去掉该参数看是否能避开这种OOM。
======================================================================
2、-XX:+DisableExplicitGC 与 Remote Method Invocation (RMI)
看了上一个例子有没有觉得-XX:+DisableExplicitGC参数用起来很危险?那干脆完全不要用这个参数吧。又有什么坑呢?
前段时间有个应用的开发来抱怨,说某次升级JDK之前那应用的GC状况都很好,很长时间都不会发生full GC,但升级后发现每一小时左右就会发生一次。经过对比发现,升级的同时也吧启动参数改了,把原本有的-XX:+DisableExplicitGC给去掉了。
观察到的日志有这么一个特征:
(后面回头再补…先睡觉去了)
发表评论
-
高效的Java异常处理框架
2009-06-23 10:58 1364一、 异常的概念和Java异常体系结构 异常是程序 ... -
六种异常处理的陋习
2009-06-23 10:23 773你觉得自己是一个Java专 ... -
HTML Response ContentType 大全
2009-06-18 13:19 976".*"="applicatio ... -
Oracle入门基本知识一点通
2009-02-11 16:00 1035引自http://www.oraclebbs.com/ ... -
javaFx环境变量配置
2009-02-09 11:01 39991)安装javafx_sdk-1_0_1-windows-i5 ... -
部署Log4j
2009-02-04 17:20 8471、拷贝"log4j-1.2.7.jar" ... -
eclipse debug 入门
2009-02-04 17:20 19891.Step Into (also F5) 跳入 2.Step ... -
AbstractWizardFormController 实例
2009-02-04 17:19 2034由于工作需要,需要经多个表单在最后一部进行操作,觉得sprin ... -
学习笔记:DB2 9 管理 - 2
2009-02-04 17:18 947DB2 9 管理 第 1 部分:服务器管理 二、DB2 客 ... -
学习笔记:DB2 9 管理 - 1
2009-02-04 17:17 680DB2 9 管理 第 1 部分:服务器管理 一、DB2 实 ... -
面向对象程序设计的61条原则收藏
2009-02-04 17:16 712(1)所有数据都应该隐藏在所在的类的内部。 (2)类的使 ... -
POI读写Excel文件
2009-02-04 17:15 1649约定:POI项目2.0版现在已经接近正式发行阶段,开发进度迅速 ... -
GBK字符集知识收藏
2009-02-04 17:14 1207GB码与BIG5是中国人常用 ... -
J2EE程序中的SQL语句自动构造方法收藏
2009-02-04 17:12 688INSERT、DELETE、UPDATE 三种SQL语句是数据 ... -
oracle9i存储过程、触发器、函数简单实例(基于PL/SQL7.1)收藏
2009-02-04 17:11 2051第一:已经存在表PUB_T_DIVISION_TEST,可以使 ... -
oracle 函数大全收藏
2009-02-04 17:11 951常用oracle函数 SQL中的单记录函数 1.ASCII ... -
PLSQL循序渐进全面学习教程(全)收藏
2009-02-04 17:08 2128康师傅 2008年01月24日( ... -
oracle系统命令收藏
2009-02-04 17:06 8980 查看表的结构其中的一种做法: select COL ... -
oracle的SQLPLUS命令大全收藏
2009-02-04 17:04 1678Oracle的sql*plus是与oracle ...
相关推荐
3. **JVM调优“标准参数”的陷阱**:R大的文章详细介绍了在不同JDK版本下JVM调优过程中可能遇到的一些陷阱。尽管该文章最初是在JDK 6时撰写的,但是其中提到的很多原则仍然适用,并且随着JDK版本的更新,这些原则也...
5. **性能调优**:涵盖JVM性能监控工具的使用,如JConsole、VisualVM等,以及如何通过调整JVM参数(如-Xms, -Xmx, -XX:NewRatio等)来优化内存分配和垃圾收集策略。 6. **线程分析**:讲解如何理解和诊断线程问题,...
10. 描述如何优化Java服务器的性能,包括JVM调优和数据库优化。 通过深入理解和实践这些Java陷阱,不仅可以避免在编程中犯错,也能在面试中展现出专业技能,为你的职业生涯加分。不断学习和探索,使你在Java的世界...
通过学习这些规范,开发者可以更准确地理解代码的运行机制,避免常见的编程陷阱,写出更高效、更易于维护的代码。 在Java7中,引入了try-with-resources语句,使得资源管理更加简洁和安全。Java8引入了lambda表达式...
7. **JVM调优**:理解JVM内存模型,如堆内存、栈内存、方法区等,以及如何进行性能优化,包括堆大小设置、类加载机制等。 8. **IO/NIO/BIO**:理解三种I/O模型的特点,尤其是NIO(非阻塞I/O)在高性能服务器编程中...
4. **JVM调优** - 面试陷阱:面试官可能会要求解释JVM的内存模型,或者如何设置JVM参数进行性能优化。 - 解析:Java虚拟机有堆内存(包括新生代、老年代)、方法区、栈等区域。理解各区域的作用,如如何设置-Xms、...
8. **JVM调优**:理解JVM的工作原理和调优策略对优化Java应用至关重要。书中可能涵盖堆内存设置、垃圾收集器选择、类加载机制等方面的常见问题。 9. **多线程同步机制**:synchronized、volatile、java.util....
4. **JVM优化**:熟悉JVM参数调优,如-Xms、-Xmx、-XX:NewRatio等。理解类加载器的双亲委派模型。 5. **集合框架**:深入理解ArrayList、LinkedList、HashSet、HashMap等集合类的内部实现,包括它们的时间复杂度和...
总结来说,这份文档是一份相当全面的Java问题定位与性能分析指南,覆盖了从基础的问题诊断方法到高级的性能调优策略,以及内存管理、并发编程和JVM调优等核心知识。对于任何从事Java开发和性能调优的工程师来说,这...
Java问题定位技术涉及到多方面知识点,从JVM到多线程、高并发以及性能调优工具等都是深入理解Java性能问题的核心组成部分。下面详细介绍这些知识点。 首先,JVM(Java虚拟机)是运行Java字节码的虚拟机进程,它是...
5. **JVM优化**:了解JVM的运行机制,包括类加载、内存模型、垃圾收集器和性能调优,这些都是面试中常见的问题。例如,理解堆内存的分代模型,知道如何分析和调整JVM参数以优化应用性能。 6. **集合框架**:深入...
### Java虚拟机(JVM)工作原理深度解析 Java虚拟机(JVM)是Java技术的核心组件之...通过对JVM工作原理的深入了解,开发人员能够更有效地编写和优化Java应用程序,利用JVM提供的强大功能,同时避免常见的陷阱和性能瓶颈。
Twitter的JVM性能调优经验分享(Attila Szegedi) pdf Web请求异步处理和海量数据即时分析在淘宝开放平台的实践 岑文初 pdf 把大象放进冰箱 技术型复杂项目的特性裂解 pdf 阿里巴巴 B2B 的服务框架探索 钱霄 pdf...
这本书详尽地阐述了如何理解和改善Java应用程序的性能,包括JVM(Java虚拟机)的工作原理、性能分析工具的使用以及实际调优策略。 在Java性能优化中,了解JVM的工作机制至关重要。JVM是Java程序运行的基础,它负责...
9. **JVM优化**:分析垃圾回收机制、内存模型、类加载机制,提供JVM调优策略。 10. **设计模式**:介绍常见的设计模式,如工厂模式、单例模式、观察者模式等,帮助开发者解决常见问题。 11. **Spring框架**:涵盖...
11. **Java虚拟机(JVM)**:理解JVM的工作原理,包括类加载机制、内存模型(堆、栈、方法区等)以及JVM调优,有助于提升程序性能。 12. **Java 8及以后的特性**:从Java 8开始,引入了Lambda表达式、Stream API和...
这部分内容可能涵盖了Java开发中的各种实践经验和技巧,比如性能优化、异常处理、内存管理、多线程同步、IO流操作、JVM调优等方面。在实际开发中,理解这些经验能够帮助开发者避免常见的陷阱,提升代码质量。例如,...
- **JVM调优**:JVM内存模型、GC算法、性能监控工具。 4. **设计模式** - **工厂模式**:单例模式、工厂方法模式、抽象工厂模式。 - **结构模式**:适配器模式、代理模式、装饰者模式等。 - **行为模式**:观察...
如今,通过合理避免常见性能陷阱,任何Java程序都能达到令人满意的运行速度。本书提供了详尽的指导,帮助开发者进行性能调优。 #### 三、调优流程详解 ##### 3.1 设定目标 在开始性能调优之前,明确调优的目标至关...