文档章节

【转发】关于Java性能的9个谬论

吴小编
 吴小编
发布于 2015/02/06 14:58
字数 3893
阅读 18
收藏 0
点赞 0
评论 0

Java的性能有某种黑魔法之称。部分原因在于Java平台非常复杂,很多情况下问题难以定位。然而在历史上还有一种趋势,人们靠智慧和经验来研究Java性能,而不是靠应用统计和实证推理。在这篇文章中,我希望拆穿一些最荒谬的技术神话。

1.Java很慢

关于Java的性能有很多谬论,这一条是最过时的,可能也是最为明显的。

确实,在上世纪90年代和本世纪初处,Java有时是很慢。

然而从那以后,虚拟机和JIT技术已经有了十多年的改进,Java的整体性能现在已经非常好了。

在6个独立的Web性能基准测试中,Java框架在24项测试中有22项位列前四。

尽管JVM利用性能剖析仅优化常用的代码路径,但这种优化效果很明显。很多情况下,JIT编译的Java代码和C++一样快,而且这样的情况越来越多了。

尽管如此,依然有人认为Java平台很慢,这或许源自体验过Java平台早期版本的人的历史偏见。

在下结论之前,我们建议保持客观的态度,并且评估一下最新的性能结果。

2.可以孤立地看待单行Java代码

考虑下面这行短小的代码:

MyObject obj = new MyObject();

对Java开发者而言,看似很明显,这行代码一定会分配一个对象并调用适当的构造器。

我们也许可以据此推出性能边界了。我们认为这行代码一定会导致执行一定量的工作,基于这种推定,就可以尝试计算其性能影响了。

其实这种认识是错误的,它让我们先入为主地认为,不管什么工作,在任何情况下都会进行。

事实上,javac和JIT编译器都能够将死代码优化掉。就JIT编译器而言,基于性能剖析数据,甚至可以通过预测将代码优化掉。在这样的情况下,这行代码根本不会运行,所以不会影响性能。

此外,在某些JVM中——比如JRockit——JIT编译器甚至可以将对象上的操作分解,这样即便代码路径还有效,分配操作也可以避免。

这里的寓意是,在处理Java性能问题时,上下文非常重要,过早的优化有可能产生违反直觉的结果。所以最好不好过早优化。相反,应该总是构建代码,并且使用性能调校技术来定位性能热点,然后加以改进。

3.微基准测试和你想象的一样

正如我们上面看到的那样,检查一小段代码不如分析应用的整体性能来的准确。

尽管如此,开发者还是喜欢编写微基准测试。似乎对平台底层的某些方面进行修修补补会带来无穷的乐趣。

理查德·费曼曾经说过:“不要欺骗自己,你自己正是最容易被欺骗的人。”这句话用来说明编写Java微基准测试这件事是再合适不过了。

编写良好的微基准测试极其困难。Java平台非常复杂,而且很多微基准测试只能用于测量瞬时效应,或是Java平台的其他意想不到的方面。

例如,如果没有经验,编写的微基准测试往往就是测一下时间或垃圾收集,却没有抓住真正的影响因素。

只有那些有实际需求的开发者和开发团队才应该编写微基准测试。这些基准测试应该完全公开(包括源代码),而且是可以复现的,还应接受同行评审及进一步的审查。

Java平台的很多优化表明统计运行和单次运行对结果影响很大。要得到真实可靠的答案,应该将一个单独的基准测试运行多次,然后把结果汇总到一起。

如果读者感觉有必要编写微基准测试,Georges、Buytaert和Eeckhout等人的论文《利用严格的统计方法评测Java 性能(Statistically Rigorous Java Performance Evaluation)》是个不错的开始。缺乏适当的统计分析,我们很容易被误导。

有很多开发好的工具以及围绕这些工具的社区(比如Google的Caliper)。如果确实有必要编写微基准测试,那也不要自己编写,这时需要的是同行的意见和经验。

4.算法慢是性能问题的最常见原因

在开发者之间有一个很常见的认知错误(普通大众也是如此),即认为系统中他们控制的那部分很重要。

在探讨Java性能时,这种认知错误也有所体现:Java开发者认为算法的质量是性能问题的主要原因。开发者考虑的是代码,因此他们自然会偏向于考虑自己的算法。

实际上在处理一系列现实中的性能问题时,人们发现算法设计是根本问题的几率不足10%。

相反,与算法相比,垃圾收集、数据库访问和配置错误导致应用程序缓慢的可能性更大。

大部分应用处理的数据量相对较小,因此,即使主要算法效率不高,通常也不会导致严重的性能问题。可以肯定,我们的算法不是最优的;尽管如此,算法带来的性能问题还是算小的,更多性能问题是应用栈的其他部分导致的。

因此我们的最佳建议是,使用实际生产数据来揭开性能问题的真正原因。要测量性能数据,而不是凭空猜测!

5.缓存可以解决所有问题

“计算机科学中的所有问题都可以通过引入一个中间层来解决。”

David Wheeler的这句程序员格言(在互联网上,这句话至少还被认为是其他两位计算机科学家说的)非常常见,尤其是在Web开发者之中很流行。

如果未能透彻理解现有的架构,而且分析也已停顿,往往就是“缓存可以解决所有问题”这种谬论抬头的时候了。

在开发者看来,与其处理吓人的现有系统,还不如在前面加一层缓存,将现有系统隐藏起来,以此期待最好的情况。无疑,这种方式只是让整体架构更复杂了,当下一个接手的开发者打算了解系统现状时,情况会更糟糕。

规模庞大、设计拙劣的系统往往缺乏整体的设计,是一次一行代码、一个子系统这样写出来的。然而很多情况下,简化并重构架构会带来更好的性能,而且几乎总是更容易让人理解。

所以当评估是否真的有必要加入缓存时,应该先计划收集一些基本的使用统计信息(比如命中率和未命中率等),以此证明缓存层带来的真正价值。

6.所有应用都需要关注Stop-The-World问题

Java平台存在一个无法改变的事实:为运行垃圾收集,所有应用线程必须周期性停顿。有时这被当作Java的一个严重缺点,即使没有任何真凭实据。

实证研究表明,如果数字数据(如价格波动)变化的频率超过200毫秒一次,人就无法正常感知了。

应用主要是给人用的,因此我们有一个有用的经验法则,200毫秒或低于200毫秒的Stop-The-World(STW)通常是没有影响的。有些应用可能有更高的要求(如流媒体),但很多GUI应用是不需要的。

少数应用(比如低延迟交易或机械控制系统)无法接受200毫秒的停顿。除非编写的就是这类应用,否则用户基本感觉不到垃圾收集器的影响。

值得一提的是,在应用线程数量超过物理核数的任何系统中,操作系统必须控制对CPU的分时访问。Stop-The-World听着可怕,但实际上任何应用(不管是JVM还是其他应用)都要面对稀缺计算资源的争用问题。

如果不去测量,JVM对应用性能有何附加影响是不清楚的。

总之,请打开GC日志,以此来确定停顿时间是否真的影响了应用。通过分析日志来确定停顿时间,这里既可以手工分析,也可以利用脚本或工具分析。然后再判定它们是否真的给应用于带来了问题。最重要的是,问自己一个关键的问题:确实有用户抱怨吗?

7.手写对象池适合一大类应用

认为Stop-The-World停顿在某种程度上是不好的,应用开发团队的一个常见反应就是在Java堆内实现自己的内存管理技术。这往往会归结为实现一个对象池(甚至是全面的引用计数),而且需要使用了领域对象的任何代码都参与进来。

这种技术几乎总是具有误导性的。它基于过去的认知,那时对象分配非常昂贵,而修改对象则廉价的多。现在的情况已经完全不同了。

现在的硬件在分配时非常高效;最新的桌面或服务器硬件,内存带宽至少是2到3GB。这是一个很大的数字,除非专门编写的应用,否则要充分利用这么大的带宽还真不容易。

一般来说,正确实现对象池非常困难(尤其是有多个线程工作时),而且对象池还带来了一些负面的要求,使这种技术不是一个通用的良好选择:

  • 所有接触到对象池代码的开发者必须了解对象池,而且能正确处理

  • 哪些代码知道对象池,哪些代码不知道对象池,其界限必须让大家知道,并且写在文档中

  • 这些额外的复杂性要保持更新,而且定期复审

  • 如果有一条不满足,悄然出现问题(类似于C 中的指针复用)的风险就又回来了

总之,只有GC停顿不能接受,而且调校和重构也未能将停顿减小到可以接受的水平时,才能使用对象池。

8.在垃圾收集中,相对于Parallel Old,CMS总是更好的选择

Oracle JDK默认使用一个并行的Stop-The-World收集器来收集老年代,即Parallel Old收集器。

Concurrent-Mark-Sweep (CMS)是一个备选方案,在大部分垃圾收集周期,它允许应用线程继续运行,但这是有代价的,而且有一些注意事项。

允许应用线程与垃圾收集线程一起运行,不可避免地带来一个问题:应用线程修改了对象图,可能会影响对象的存活性。这种情况必须在事后加以清理,因此CMS实际上有两个STW阶段(通常非常短)。

这会带来一些后果:

  1. 必须将所有应用线程带到安全点,每次Full GC期间会停顿两次;

  2. 尽管垃圾收集与应用同时执行,但应用的吞吐量会降低(通常是50%);

  3. 在使用CMS进行垃圾收集时,JVM所用的簿记信息(和CPU周期)远高于其他的并行收集器。

这些代价是不是物有所值,取决于应用的情况。但是天下没有免费的午餐。CMS收集器在设计上值得称道,但它不是万能的。

所以在确定CMS是正确的垃圾收集策略之前,首先应该确认Parallel Old的STW停顿确实不能接受,而且已经无法调校。最后,我重点强调一下,所有指标必须从与生产系统等价的系统中获得。

9.增加堆的大小可以解决内存问题

当应用陷入困境,并且怀疑是GC的问题时,很多应用团队的反应就是增加堆的大小。在某些情况下,这样做可以快速见效,而且为我们留出了时间来考虑更周详的解决方案。然而,如果没有充分理解性能问题的原因,这种策略反而会让事情变得更糟糕。

考虑一个编码非常糟糕的应用程序,它正在产生很多领域对象 (它们的生存时间很有代表性,比如说是2-3秒)。如果分配率高到一定程度,垃圾收集会频繁进行,这样领域对象会被提升到老年代。领域对象几乎是一进入年老代,生存时间就结束了,从而直接死亡,但它们直到下一次Full GC时才会被回收。

如果增加了应用的堆大小,我们所做的不过是增加了相对短命的对象进入和死亡所用的空间。这会导致Stop-The-World停顿时间更长,对应用并无益处。

在修改堆大小或者调校其他参数之前,理解对象的分配和生存时间的动态是很有必要的。没有测量性能数据就盲目行动,只会使情况更糟糕。在这里,垃圾收集器的老年代分布情况特别重要。

结论

当谈到Java的性能调校时,直觉常常起误导作用。我们需要实验数据和工具来帮助我们将平台的行为可视化并加强理解。

垃圾收集就是最好的例子。对于调校或者生成指导调校的数据而言,GC子系统拥有无限的潜力;但是对于产品应用而言,不使用工具很难理解所产生数据的意义。

默认情况下,运行任意Java进程(包括开发环境和产品环境),应该至少总是使用如下参数:

-verbose:gc(打印GC日志)
-Xloggc:(更全面的GC日志)
-XX:+PrintGCDetails(更详细的输出)
-XX:+PrintTenuringDistribution(显示JVM所使用的将对象提升进入老年代的年龄阈值)

然后使用工具来分析日志,这里可以利用手写的脚本,可以用图生成,还可以使用GCViewer(开源的)或jClarity Censum这样的可视化工具。

【支持原创】转发自:http://www.codeceo.com/article/9-error-java-performance.html


© 著作权归作者所有

共有 人打赏支持
吴小编
粉丝 18
博文 65
码字总数 47867
作品 0
海淀
深入理解JVM学习笔记(一、总览)

1、JVM历史 2、JVM内存结构 3、JVM垃圾回收机制 4、JVM性能监控工具 5、JVM性能调优案例时间 6、JVM类文件结构 7、JVM类加载机制 8、JVM字节码执行引擎 9、JVM虚拟机编译及其运行时优化 10、...

jintaohahahaha ⋅ 05/28 ⋅ 0

OpenJ9 和 HotSpot 的对比 Part 1

OpenJ9 和 IBM J9 是来自默认 Oracle HotSpot JVM 的不同 JVM 实现。使用现代的 adoptopenjdk 预置 Docker 镜像,你可以轻易地切换和测试不同的组合,并且可以为你选择合适的 JVM。 这个传言...

oschina ⋅ 05/28 ⋅ 0

Oracle Java Mission Control 帮助

缩写 含义 JDK Java 开发工具包 JDP Java Discovery Protocol JFR Java 飞行记录器 JMC Java Mission Control JMX Java Management Extensions JVM Java 虚拟机 MBean 托管 Bean (Java) RCP ......

光斑 ⋅ 04/27 ⋅ 0

《成神之路-基础篇》JVM——JVM参数及调优(已完结)

Java内存模型,Java内存管理,Java堆和栈,垃圾回收 本文是[《成神之路系列文章》][1]的第一篇,主要是关于JVM的一些介绍。 持续更新中 JVM参数及调优 JVM实用参数系列 成为Java GC专家(5)...

⋅ 05/05 ⋅ 0

Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!

前几天写了一篇 Java 8 即将在 2019 年停止免费向企业提供更新的文章,企图迫使用户向更新一代的 Java 版本升级,但让人遗憾的是,小编今天收到了 Oracle Java 版本的升级推送,装完居然是 ...

Java技术栈 ⋅ 04/27 ⋅ 0

6.1 Java vs php 优缺点、适用类型

1、上手难度比较 php易学易懂,非技术的人,稍稍学习,可以上手,环境搭建用LAMP/WAMP一键安装包,常见开发工具,phpstorm。 Java则要求理解,Java语法和核心架构(jar包)以及JVM,环境配置也...

产品经理的技术课堂 ⋅ 05/20 ⋅ 0

JVM 即时编译器 - GraalVM

Graal — 新的 JVM 即时编译器 GraalVM 是新一代的高性能跨语言虚拟机,用于运行 JavaScript、Python 3、Ruby、R、基于 JVM 的语言,如 Java、Scala、Kotlin 和基于 LLVM 的语言,如 C 和 C+...

匿名 ⋅ 2014/09/29 ⋅ 2

JDK 11 特性抢先看:5 月新增三个 JEP

一周前(2018年5月7日),JDK11 新增了三个 JEP 。在 jdk-dev 邮件列表中出现了三封邮件,Mark Reinhold 发表了以下公告: JDK 11 实现了 JEP:324:关于 Curve25519 和 Curve448 的重要协议...

oschina ⋅ 05/16 ⋅ 0

00.java虚拟机的基本结构概念

java虚拟机的基本结构 1、类加载子系统:负责从文件系统或者网络中加载Class信息,加载的信息存放在一块称之为方法区的内存空间。 2、方法区:就是存放类信息、常量信息、常量池信息、包括字...

挚爱冷如烟° ⋅ 05/08 ⋅ 0

主流Java数据库连接池比较及前瞻

本文转载自微信公众号「工匠小猪猪的技术世界」 主流数据库连接池 常用的主流开源数据库连接池有C3P0、DBCP、Tomcat Jdbc Pool、BoneCP、Druid等 C3p0: 开源的JDBC连接池,实现了数据源和JND...

渣渣(Charles) ⋅ 04/30 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

笔试题之Java基础部分【简】【一】

基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法,虚拟机方面的语法,其他 1.length、length()和size() length针对...

anlve ⋅ 18分钟前 ⋅ 1

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 43分钟前 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 47分钟前 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 8

Java 开发者不容错过的 12 种高效工具

Java 开发者常常都会想办法如何更快地编写 Java 代码,让编程变得更加轻松。目前,市面上涌现出越来越多的高效编程工具。所以,以下总结了一系列工具列表,其中包含了大多数开发人员已经使用...

jason_kiss ⋅ 昨天 ⋅ 0

Linux下php访问远程ms sqlserver

1、安装freetds(略,安装在/opt/local/freetds 下) 2、cd /path/to/php-5.6.36/ 进入PHP源码目录 3、cd ext/mssql进入MSSQL模块源码目录 4、/opt/php/bin/phpize生成编译配置文件 5、 . ./...

wangxuwei ⋅ 昨天 ⋅ 0

如何成为技术专家

文章来源于 -- 时间的朋友 拥有良好的心态。首先要有空杯心态,用欣赏的眼光发现并学习别人的长处,包括但不限于工具的使用,工作方法,解决问题以及规划未来的能力等。向别人学习的同时要注...

长安一梦 ⋅ 昨天 ⋅ 0

Linux vmstat命令实战详解

vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。这个命令是我查看Linux/Unix最喜爱的命令...

刘祖鹏 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部