- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java虚拟机JVM性能优化(一):JVM知识总结由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
Java应用程序是运行在JVM上的,但是你对JVM技术了解吗?这篇文章(这个系列的第一部分)讲述了经典Java虚拟机是怎么样工作的,例如:Java一次编写的利弊,跨平台引擎,垃圾回收基础知识,经典的GC算法和编译优化。之后的文章会讲JVM性能优化,包括最新的JVM设计——支持当今高并发Java应用的性能和扩展.
如果你是一个开发人员,你肯定遇到过这样的特殊感觉,你突然灵光一现,所有的思路连接起来了,你能以一个新的视角来回想起你以前的想法。我个人很喜欢学习新知识带来的这种感觉。我已经有过很多次这样的经历了,在我使用JVM技术工作时,特别是使用垃圾回收和JVM性能优化时。在这个新的Java世界中,我希望和你分享我的这些启发。希望你能像我写这篇文章一样兴奋的去了解JVM的性能.
这个系列文章,是为所有有兴趣去学习更多JVM底层知识,和JVM实际做了什么的Java开发人员所写的。在更高层次,我将讨论垃圾回收和在不影响应用运行的情况下,对空闲内存安全和速度上的无止境追求。你将学到JVM的关键部分:垃圾回收和GC算法,编译优化,和一些常用的优化。我同样会讨论为什么Java标记这样难,提供建议什么时候应该考虑测试性能。最后,我将讲一些JVM和GC的新的创新,包括Azul's Zing JVM, IBM JVM, 和Oracle's Garbage First (G1) 垃圾回收中的重点.
我希望你读完这个系列时对Java可扩展性限制的特点有更深的了解,同样的这样限制是如何强制我们以最优的方式创建一个Java部署。希望你会有一种豁然开朗的感受,并且能激发了一些好的Java灵感:停止接受那些限制,并去改变它!如果你现在还不是一个开源工作者,这个系列或许会鼓励你往这方面发展.
JVM性能和“一次编译,到处运行”的挑战 。
我有新的消息告诉那些固执的认为Java平台本质上是缓慢的人。当Java刚刚做为企业级应用的时候,JVM被诟病的Java性能问题已经是十几年前的事了,但这个结论,现在已经过时了。这是真的,如果你现在在不同的开发平台上运行简单静态和确定的任务时,你将很可能发现使用机器优化过的代码比使用任何虚拟环境执行的要好,在相同的JVM下。但是,Java的性能在过去10年有了非常大的提升。Java产业的市场需求和增长,导致了少量的垃圾回收算法、新的编译创新、和大量的启发式方法和优化,这些使JVM技术得到了进步。我将在以后的章节中介绍一些.
JVM的技术之美,同样是它最大的挑战:没有什么可以被认为是“一次编译,到处运行”的应用。不是优化一个用例,一个应用,一个特定的用户负载,JVM不断的跟踪Java应用现在在做什么,并进行相应的优化。这种动态的运行导致了一系列动态的问题。当设计创新时(至少不是在我们向生产环境要性能时),致力于JVM的开发者不会依赖静态编译和可预测的分配率.
JVM性能的事业 。
在我早期的工作中我意识到垃圾回收是非常难“解决”的,我一直着迷于JVMs和中间件技术。我对JVMs的热情开始于我在JRockit团队中时,编码一种新的方法用于自学,自己调试垃圾回收算法(参考 Resources)。这个项目(转变为JRockit一个实验性的特点,并成为Deterministic Garbage Collection算法的基础)开启了我JVM技术的旅程。我已经在BEA系统、Intel、Sun和Oracle(因为Oracle收购BEA系统,所以被Oracle短暂的工作过)工作过。之后我加入了在Azul Systems的团队去管理Zing JVM,现在我为Cloudera工作.
机器优化的代码可能会实现较好的性能(但这是以牺牲灵活性来做代价的),但对于动态装载和功能快速变化的企业应用这并不是一个权衡选择它的理由。大多数的企业为了Java的优点,更愿意去牺牲机器优化代码带来的勉强完美的性能.
1.易于编码和功能开发(意义是更短的时间去回应市场) 2.得到知识渊博的的程序员 3.用Java APIs和标准库更快速的开发 4.可移植性——不用为新的平台去重新写Java应用 。
从Java代码到字节码 。
做为一个Java程序员,你可能对编码、编译和执行Java应用很熟悉。例子:我们假设你有一个程序(MyApp.java),现在你想让它运行。去执行这个程序你需要先用javac(JDK内置的静态Java语言到字节码编译器)编译。基于Java代码,javac生成相应的可执行字节码,并保存在相同名字的class文件:MyApp.class中。在把Java代码编译成字节码后,你可以通过java命令(通过命令行或startup脚本,使用不使用startup选项都可以)来启动可执行的class文件,从而运行你的应用。这样你的class被加载到运行时(意味着Java虚拟机的运行),程序开始执行.
这就是表面上每一个应用执行的场景,但是现在我们来探究下当你执行java命令时究竟发生了什么。Java虚拟机是什么?大多数开发人员通过持续调试来与JVM交互——aka selecting 和value-assigning启动选项能让你的Java程序跑的更快,同时避免了臭名昭著的”out of memory”错误。但是,你是否曾经想过,为什么我们起初需要一个JVM来运行Java应用呢?
什么是Java虚拟机?
简单的说,一个JVM是一个软件模块,用于执行Java应用字节码并且把字节码转化到硬件,操作系统特殊指令。通过这样做,JVM允许Java程序在第一次编写后可以在不同的环境中执行,并不需要更改原始的代码。Java的可移植性是通往企业应用语言的关键:开发者并不需要为不同平台重写应用代码,因为JVM负责翻译和平台优化.
一个JVM基本上是一个虚拟的执行环境,作为一个字节码指令机器,而用于分配执行任务和执行内存操作通过与底层的交互.
一个JVM同样为运行的Java应用照看动态资源管理。这就意味着它掌握分配和释放内存,在每个平台上保持一致的线程模型,在应用执行的地方用一种适于CPU架构的方式组织可执行的指令。JVM把开发人员从跟踪对象当中的引用,和它们需要在系统中存在多长时间中解放出来。同样的它不用我们管理何时去释放内存——一个像C语言那样的非动态语言的痛点.
你可以把JVM当做是一个专门为Java运行的操作系统;它的工作是为Java应用管理运行环境。一个JVM基本上是一个虚拟的通过与底层的交互的执行环境,作为一个字节码指令机器,而用于分配执行任务和执行内存操作.
JVM组件概述 。
有很多写JVM内部和性能优化的文章。作为这个系列的基础,我将会总结概述下JVM组件。这个简短的阅览会为刚接触JVM的开发者有特殊的帮助,会让你更想了解之后更深入的讨论.
从一种语言到另一种——关于Java编译器 。
编译器是把一种语言输入,然后输出另一种可执行的语句。Java编译器有两个主要任务:
1. 让Java语言更加轻便,不用在第一次写的时候固定在特定的平台; 。
2. 确保对特定的平台产生有效的可执行的代码.
编译器可以是静态也可以是动态。一个静态编译的例子是javac。它把Java代码当做输入,并转化为字节码(一种在Java虚拟机执行的语言)。静态编译器一次解释输入的代码,输出可执行的形式,这个是在程序执行时将被用到。因为输入是静态的,你将总能看到结果相同。只有当你修改原始代码并重新编译时,你才能看到不同的输出.
动态编译器,例如Just-In-Time (JIT)编译器,把一种语言动态的转化为另一种,这意味着它们做这些时把代码被执行。JIT编译器让你收集或创建运行数据分析(通过插入性能计数的方式),用编译器决定,用手边的环境数据。动态的编译器可以在编译成语言的过程之中,实现更好的指令序列,把一系列的指令替换成更有效的,甚至消除多余的操作。随着时间的增长你将收集更多的代码配制数据,做更多更好的编译决定;整个过程就是我们通常称为的代码优化和重编译.
动态编译给了你可以根据行为去调整动态的变化的优势,或随着应用装载次数的增加催生的新的优化。这就是为什么动态编译器非常适合Java运行。值得注意的是,动态编译器请求外部数据结构,线程资源,CPU周期分析和优化。越深层次的优化,你将需要越多的资源。然而在大多数环境中,顶层对执行性能的提升帮助非常小——比你纯粹的解释要快5到10倍的性能.
分配会导致垃圾回收 。
分配在每一个线程基于每个“Java进程分配内存地址空间”,或者叫Java堆,或者直接叫堆。在Java世界中单线程分配在客户端应用程序中很常见。然而,单线程分配在企业应用和工作装载服务端变的没有任何益处,因为它并没有使用现在多核环境的并行优势.
并行应用设计同样迫使JVM保证在同一时间,多线程不会分配同一个地址空间。你可以通过在整个分配空间中放把锁来控制。但这种技术(通常叫做堆锁)很消耗性能,持有或排队线程会影响资源利用和应用优化的性能。多核系统好的一面是,它们创造了一个需求,为各种各样的新的方法在资源分配的同时去阻止单线程的瓶颈,和序列化.
一个常用的方法是把堆分成几部分,在对应用来说每个合式分区大小的地方——显然它们需要调优,分配率和对象大小对不同应用来说有显著的变化,同样线程的数量也不同。线程本地分配缓存(Thread Local Allocation Buffer,简写:TLAB),或者有时,线程本地空间(Thread Local Area,简写:TLA),是一个专门的分区,在其中线程不用声明一个全堆锁就可以自由分配。当区域满的时候,堆就满了,表示堆上的空闲空间不够用来放对象的,需要分配空间。当堆满的时候,垃圾回收就会开始.
碎片 。
使用TLABs捕获异常,是把堆碎片化来降低内存效率。如果一个应用在要分配对象时正巧不能增加或者不能完全分配一个TLAB空间,这将会有空间太小而不能生成新对象的风险。这样的空闲空间被当做“碎片”。如果应用程序一直保持对象的引用,然后再用剩下的空间分配,最后这些空间会在很长一段时间内空闲.
碎片就是当碎片被分散在堆中的时候——通过一小段不用的内存空间来浪费堆空间。为你的应用分配 “错误的”TLAB空间(关于对象的大小、混合对象的大小和引用持有率)是导致堆内碎片增多的原因。在随着应用的运行,碎片的数量会增加在堆中占有的空间。碎片导致性能下降,系统不能给新应用分配足够的线程和对象。垃圾回收器在随后会很难阻止out-of-memory异常.
TLAB浪费在工作中产生。一种方法可以完全或暂时避免碎片,那就是在每次基础操作时优化TLAB空间。这种方法典型的作法是应用只要有分配行为,就需要重新调优。通过复杂的JVM算法可以实现,另一种方法是组织堆分区实现更有效的内存分配。例如,JVM可以实现free-lists,它是连接起一串特定大小的空闲内存块。一个连续的空闲内存块和另一个相同大小的连续内存块相连,这样会创建少量的链表,每个都有自己的边界。在有些情况下free-lists导致更好的合适内存分配。线程可以对象分配在一个差不多大小的块中,这样比你只依靠固定大小的TLAB,潜在的产生少的碎片.
GC琐事 。
有一些早期的垃圾收集器拥有多个老年代,但是当超过两个老年代的时候会导致开销超过价值。另一种优化分配减少碎片的方法,就是创造所谓的新生代,这是一个专门用于分配新对象的专用堆空间。剩余的堆会成为所谓的老年代。老年代是用来分配长时间存在的对象的,被假定会存在很长时间的对象包括不被垃圾收集的对象或者大对象。为了更好的理解这种分配的方法,我们需要讲一些垃圾收集的知识.
垃圾回收和应用性能 。
垃圾回收是JVM的垃圾回收器去释放没有引用的被占据的堆内存。当第一次触发垃圾收集时,所有的对象引用还被保存着,被以前的引用占据的空间被释放或重新分配。当所有可回收的内存被收集后,空间等待被抓取和再次分配给新对象.
垃圾回收器永远都不能重声明一个引用对象,这样做会破坏JVM的标准规范。这个规则的异常是一个可以捕获的soft或weak引用 ,如果垃圾收集器将要将近耗尽内存。我强烈推荐你尽量避免weak引用,然而,因为Java规范的模糊导致了错误的解释和使用的错误。更何况,Java是被设计为动态内存管理,因为你不需要考虑什么时候和什么地方释放内存.
垃圾收集器的一个挑战是在分配内存时,需要尽量不影响运行着的应用。如果你不尽量垃圾收集,你的应用将耗近内存;如果你收集的太频繁,你将损失吞吐量和响应时间,这将对运行的应用产生坏的影响.
GC算法 。
有许多不同的垃圾回收算法。稍后,在这个系列里将深入讨论几点。在最高层,垃圾收集两个最主要的方法是引用计数和跟踪收集器.
引用计数收集器会跟踪一个对象指向多少个引用。当一个对象的引用为0时,内存将被立即回收,这是这种方法的优点之一。引用计数方法的难点在于环形数据结构和保持所有的引用即时更新.
跟踪收集器对仍在引用的对象标记,用已经标记的对象,反复的跟随和标记所有的引用对象。当所有的仍然引用的对象被标记为“live”时,所有的不被标记的空间将被回收。这种方法管理环形数据结构,但是在很多情况下收集器应该等待直到所有标记完成,在重新回收不被引用的内存之前.
有不种的途径来被上面的方法。最著名的算法是 marking 或copying 算法, parallel 或 concurrent算法。我将在稍后的文章中讨论这些.
通常来说垃圾回收的意义是致力于在堆中给新对象和老对象分配地址空间。其中“老对象”是指在许多垃圾回收后幸存的对象。用新生代来给新对象分配,老年代给老对象,这样能通过快速回收占据内存的短时间对象来减少碎片,同样通过把长时间存在的对象聚合在一起,并把它们放到老年代地址空间中。所有这些在长时间对象和保存堆内存不碎片化之间减少了碎片。新生代的一个积极作用是延迟了需要花费更大代价回收老年代对象的时间,你可以为短暂的对象重复利用相同的空间。(老空间的收集会花费更多,是因为长时间存在的对象们,会包含更多的引用,需要更多的遍历。) 。
最后值的一提的算法是compaction,这是管理内存碎片的方法。Compaction基本来说就是把对象移动到一起,从来释放更大的连续内存空间。如果你熟悉磁盘碎片和处理它的工具,你会发现compaction跟它很像,不同的是这个运行在Java堆内存中。我将在系列中详细讨论compaction.
总结:回顾和重点 。
JVM允许可移植(一次编程,到处运行)和动态的内存管理,所有Java平台的主要特性,都是它受欢迎和提高生产力的原因.
在第一篇JVM性能优化系统的文章中我解释了一个编译器怎么把字节码转化为目标平台的指令语言的,并帮助动态的优化Java程序的执行。不同的应用需要不同的编译器.
我同样简述了内存分配和垃圾收集,和这些怎么与Java应用性能相关的。基本上,你越快的填满堆和频繁的触发垃圾收集,Java应用的占有率越高。垃圾收集器的一个挑战是在分配内存时,需要尽量不影响运行着的应用,但要在应用耗尽内存之前。在以后的文章中我们会更详细的讨论传统的和新的垃圾回收和JVM性能优化.
最后此篇关于Java虚拟机JVM性能优化(一):JVM知识总结的文章就讲到这里了,如果你想了解更多关于Java虚拟机JVM性能优化(一):JVM知识总结的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在这段令人惊叹的视频 ( https://www.youtube.com/watch?v=udix3GZouik ) 中,Alex Blom 谈到了 Ember 在移动世界中的“黑客攻击”。 在 22
我们希望通过我们的应用收集使用情况统计信息。因此,我们希望在服务器端的某个地方跟踪用户操作。 就性能而言,哪个选项更合适: 在 App Engine 请求日志中跟踪用户操作。即为每个用户操作写入一个日
在针对对象集合的 LINQ 查询的幕后究竟发生了什么?它只是语法糖还是发生了其他事情使其更有效的查询? 最佳答案 您是指查询表达式,还是查询在幕后的作用? 查询表达式首先扩展为“普通”C#。例如: v
我正在构建一个简单的照片库应用程序,它在列表框中显示图像。 xaml 是:
对于基于 Web 的企业应用程序,使用“静态 Hashmap 存储对象” 和 apache java 缓存系统有何优缺点?哪一个最有利于性能并减少堆内存问题 例如: Map store=Applica
我想知道在性能方面存储类变量的最佳方式是什么。我的意思是,由于 Children() 函数,存储一个 div id 比查找所有其他类名更好。还是把类名写在变量里比较好? 例如这样: var $inne
我已经阅读了所有这些关于 cassandra 有多快的文章,例如单行读取可能需要大约 5 毫秒。 到目前为止,我不太关心我的网站速度,但是随着网站变得越来越大,一些页面开始需要相当多的查询,例如一个页
最近,我在缓存到内存缓存之前的查询一直需要很长时间才能处理!在这个例子中,它花费了 10 秒。在这种情况下,我要做的就是获得 10 个最近的点击。 我感觉它加载了所有 125,592 行然后只返回 1
我找了几篇文章(包括SA中的一些问题),试图找到基本操作的成本。 但是,我尝试制作自己的小程序,以便自己进行测试。在尝试测试加法和减法时,我遇到了一些问题,我用简单的代码向您展示了这一点
这个问题在这里已经有了答案: Will Java app slow down by presence of -Xdebug or only when stepping through code? (
我记得很久以前读过 with() 对 JavaScript 有一些严重的性能影响,因为它可能对范围堆栈进行非确定性更改。我很难找到最近对此的讨论。这仍然是真的吗? 最佳答案 与其说 with 对性能有
我们有一个数据仓库,其中包含非规范化表,行数从 50 万行到 6 多万行不等。我正在开发一个报告解决方案,因此出于性能原因我们正在使用数据库分页。我们的报告有搜索条件,并且我们已经创建了必要的索引,但
我有一条有效的 SQL 语句,但需要很长时间才能处理 我有一个 a_log 表和一个 people 表。我需要在 people 表中找到给定人员的每个 ID 的最后一个事件和关联的用户。 SELECT
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
通常当我建立一个站点时,我将所有的 CSS 放在一个文件中,并且一次性定义与一组元素相关的所有属性。像这样: #myElement { color: #fff; background-
两者之间是否存在任何性能差异: p { margin:0px; padding:0px; } 并省略最后的分号: p { margin:0px; padding:0px } 提前致谢!
我的应用程序 (PHP) 需要执行大量高精度数学运算(甚至可能出现一共100个数字) 通过这个论坛的最后几篇帖子,我发现我必须使用任何高精度库,如 BC Math 或 GMP,因为 float 类型不
我一直在使用 javamail 从 IMAP 服务器(目前是 GMail)检索邮件。 Javamail 非常快速地从服务器检索特定文件夹中的消息列表(仅 id),但是当我实际获取消息(仅包含甚至不包含
我非常渴望开发我的第一个 Ruby 应用程序,因为我的公司终于在内部批准了它的使用。 在我读到的关于 Ruby v1.8 之前的所有内容中,从来没有任何关于性能的正面评价,但我没有发现关于 1.9 版
我是 Redis 的新手,我有一个包含数百万个成员(member) ID、电子邮件和用户名的数据集,并且正在考虑将它们存储在例如列表结构中。我认为 list 和 sorted set 可能最适合我的情
我是一名优秀的程序员,十分优秀!