JVM调优

目标:调整Java虚拟机的参数使得性能达到最优。

原则:无监控不调优。

Java内存结构

程序计数器:它是一块较小的线程私有的内存空间。它可以看做是当前线程所执行字节码的行号显示器。通过改变这个计数器的值可以选择执行的字节码。借助这个性质我们可以使用计数器来恢复被打断的线程。每一个线程都有自己的一个程序计数器互不影响。

虚拟机栈:是一块线程私有的内存空间。每起一个线程就会起一个线程栈。而在一个线程中可以调用多个方法,每起一个方法就会起一个栈帧所以一个线程栈中就会有多个栈帧。每一个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。而每一个栈帧中存放着局部变量表,操作函数等信息。我们一般粗糙的把Java中的内存分为堆和栈,里面所说的堆就是指这个堆或者是仅仅指局部变量表部分。当线程请求的栈的深度大于当前虚拟机的栈的深度的时候就会触发StackOverFlowError,当遇到能够动态扩展的虚拟机栈在扩展之后仍然没有满足当前栈的请求的话那么就会抛出OutOfMemoryError。

本地方法栈:Java访问C语言等其它语言所用到的栈,我们访问不了。调优不了。但是它从原理上与虚拟机栈相似。

堆:是最大JVM的内存同时它也是线程共享的。它里面只能存对象,但是反过来对象不一定都存在堆里后面我们会提到栈上分配。从内存分配的角度来看线程共享的Java堆可能划分出多个线程私有的分配缓冲区(也就是我们常常提到的ThreadLocal变量)。

方法区:与堆一样是一个线程共享的内存区它用于存储已经被加载的类信息、常量、静态变量等。他还有一个名字就是永久区,但是只有HotSpot虚拟机上开发才有这样的说法而其它的机是没有永久区这个概念的。

运行时常量池:他是方法区的一部分,类文件中有一项信息是常量池,它用来存放在编译期生成的字面量和符号引用。这部分内容将在类加载后在运行时常量池中存放。不仅仅可以将常量池的内容在类加载完装到运行时常量池,而且就算在编译期不在常量池的内容也能够在程序运行时装载到运行时常量池(比如说String的intren()方法)。需要注意的是:运行时常量池相对于常量池来说是动态的。

直接内存:它不是Java虚拟机中定义的内存区域。它直接通过Native函数库分配到堆外内存。

我们能够优化的地方只有堆,堆也是JVM内存最大的存储区域。

堆内存和方法区都是线程所共享的。

栈内存和本地方法栈,PC计数器,每个线程所独有的。

堆内存

Eden伊甸园:

Survivor:幸存者

新生代比例经验值:8 :1 :1

Tenured:老年代

总体比例:1 : 2  或者是 3 :7

流程:当我们第一次new出一个对象来的时候特别大的对象会放在老年代,其它的普通对象直接放在新生代。每次的survivor之间进行copy的时候另一个survivor会被回收,也就是时时刻刻都有一个survivor为空。由于这个算法是基于内存的复制所以效率很高。

如果经历了很多次的GC都没有回收的话就会被放入老年代。

GC

什么是垃圾?

引用计数算法

没有引用指向的对象就是垃圾?不完全是,比如说环形垃圾互相引用的对象。

所以使用循环引用的方法去判断垃圾是不行的。

正向可达算法

首先要得到在堆内存中一定不是垃圾的根对象,我们称之为GCRoots。顺着GCRoots的引用往下找顺藤摸瓜摸到的就是好瓜,摸不到的就是烂瓜(垃圾)。在Java中可作为GCRoots的对象有:

①虚拟机栈栈帧中的局部变量列表中的引用对象。

②方法区中静态属性引用的对象。

③方法区中常见的引用对象。

④本地方法栈中的引用对象。

拓展垃圾回收时刻

MinorGC

新建对象优先在Eden区分配,如果分配不下就会发生MinorGC。MinorGC主要是回收Eden区与Survivor中的垃圾。如果垃圾回收之后对象还是放不下的话那么就会放在老年代。

FullGC

FullGC是用来清理整个堆空间的,包括新生代老年代。所以FullGC会造成很大的开销所以要避免FullGC。

造成FullGC的常见原因以及解决方法:

①System.gc()会触发FullGC所以要避免FullGC。

②老年代的内存不足,主要是有大的对象又放了进来。这时我们可以适当地增大Survivor区、老年代的大小。

③永久代满也会触发FullGC。可以适当地增大永久带的大小或者是开启CMS回收永久代选项。

垃圾收集算法

Mark-Sweep(标记清除算法)

算法本身只是标记而不是清除,被标记可用的内存区域会在新new对象的时候可以直接占用。缺点是内存的不连续。当来了一个大的对象的时候内存中会由于碎片化而装不下这时会进行fullGC(全回收),把离散的区域压缩到一片连续的区域这时才可以放下,这样的话效率就会略低。

Copying(拷贝)

Copy会把内存区域分为两个部分A、B而且肯定有一个区域为空(假设B区域为空)。在垃圾回收的时候首先会用正向可达算法将所有的存活对象找到。然后会把A中的所有存活对象拷贝到B区域并且压缩。最后回收A区中的垃圾。在洗一次GC之前,产生的新对象会被放到B区域来如此往复。它的效率非常之高。这个算法的缺点就是浪费内存,永远会浪费掉一半的内存。

为什么eden的区域比survivor大。就是因为在eden中的对象大多数会被回收所以存活下来的对象会比较少这时就比较适合使用拷贝算法(拷贝的量比较少)。

Mark-Compact(标记压缩)

首先将幸存的对象压缩到一端然后再进行GC这样的话也会得到连续的可用的空间。效率比copy略低。这个算法常常用于老年代,在新生代用的是copy的算法。

JVM使用分代算法

New

存活对象少,使用copying占用的内存空间也不大,效率也高。

Old

垃圾少,一般使用mark-compact标记压缩。

除此之外还有Mark-cleaning(标记清理)。

补充:

当堆内存的使用率超过70%的时候,GC才会启动回收。

发生在新生代的回收 --- minor gc 初代回收

发生在老生代的回收 --- full gc 完全回收

当new出来的对象比较小的时候回方到eden区域,如果new出来的对象比较大的时候那么就会放到tenured区去。

JVM参数

- 标准参数所有的JVM都应该支持。

-X非标准参数,每个JVM都应该实现。

-XX不稳定参数(扩展参数),下一个版本可能会取消。

JVM垃圾收集器

Serial Collector

XX + UseSerialGC 序列化垃圾收集器,一个单线程的收集器,实际中使用的并不多。

Parallel Collector

并发量大,但是在每次垃圾收集的时候回导致JVM停顿。

CMS

并发收集,分区处理。停顿时间短,在垃圾收集的时候,JVM还可以运行。

G1

不仅停顿时间短(这是一个平衡点)而且并发量大。

Java对象的分配

当new出一个对象来JVM会经历这样的几个分配过程。

栈上分配(这个很颠覆)

当new出一个小的对象来的时候那么会优先分配到栈(线程栈)上面去。JavaServer模式默认会开启栈优化。因为栈中的对象会在方法结束之后栈针就会销毁,因此它就根本无需垃圾回收(它本身就有垃圾回收的特质)。

无逃逸如果在方法的外面有一个引用指向了方法内部那么此时这个方法就逃逸了。

标量替换,将一成员变量拿出来当做普通的数据类型往栈上存。

无需调整

线程本地分配(TLAB Thread Local Allacation Buffer)

当栈上分配不了也就是栈空间满了会来到线程本地分配。每一个线程在执行的时候会给自己分配一块自己专用的内存,叫做线程本地内存。(一个?)线程本地内存默认占用eden内存的1%。如果每一个线程都要放入eden的同一块区域那么这个区域就要进行加锁,但是每个线程的数据都有自己的一块独立的区域那么就不需要加锁了,不加锁就提高了访问效率。

无需调整

老年代

当上述两种都分配不了那么就先看看自己是否是一个大对象,如果是就分配到老年代。

Eden

如果自己是一个不太大的对象就分配到eden区来。

垃圾回收效率的提高

使用TLAB会提升一截,使用逃逸分析和标量替换性能又能够提升一截。

在eclipse中的run  configuration中配置:

-XX:DoEscapeAnalysis //不做逃逸分析 与栈上分配有关

-XX:-EliminateAllocations //不做标量提换 与栈上分配有关

-XX:UseTLAB// 不使用本地缓存

-XX:+PrintGC // 打印GC过程

在实际的环境中我们要权衡并发的数量和并发的深度的关系。

一个检测的实例

在实务上我们需要通过一些工具来判断在程序中造成内存溢出的原因,这里就介绍一个实例

代码如下:

package test;import java.util.ArrayList;
import java.util.List;public class Test {public static void main(String[] args) {List<Object> list = new ArrayList<Object>();for (int i = 0; i < 100000000; i++) {list.add(new byte[1024*1024]);}}
}

参数如下(在Run Configuration的时候来在VMarguments中进行设置):

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=C:\tmp\jvm.dump (生成堆内存相关的文件)
-XX:+PrintGCDetails (打印GC的详细过程)
-Xms10M (初始堆大小)
-Xmx10M  (最大堆大小)

最后会在c盘的tmp目录下面生成一个jvm.dump的文件。将jvm.dump导入到jdk文件夹下的bin目录中的jvisualvm.exe中。然后我们观测到了造成内存溢出的是由byte[]造成的,如下图所示。

Tomcat的优化配置

改动tomcat-->bin-->contalina.bat下面的的set JAVA_OPTS参数

使用Jmeter工具启动多个线程来对tomcat进行性能测试观察配置参数之前(也就是将set JAVA_OPTS中的配置注释掉)与解开注释前后每一秒并发数量的多少来判断性能的提升。

最后,在实务上我们不推荐手动使用gc()来垃圾回收,这样会破坏我们的设定回收策略。

最后,特地感谢马老师一个真正做教育的老师!

马士兵老师JVM调优(修订版)相关推荐

  1. 马士兵老师JVM调优

    JVM调优 目标:调整Java虚拟机的参数使得性能达到最优. 原则:无监控不调优. Java内存结构 虚拟机栈:存放局部变量,每起一个方法都会在栈内存中起一个栈针.所有的局部变量都方法在这个栈针中.所 ...

  2. 2022年了Java架构师怎样进阶,马士兵老师给你答案

    苦于网络上充斥的各种java知识,多半是互相抄袭,导致很多后来者在学习java知识中味同嚼蜡,今天给大家推荐马士兵老师分享的进阶成为java架构师所必须掌握的核心知识点. 废话少说,直接上正题 1.多 ...

  3. 清华毕业大牛教你涨薪5K的JVM调优骚操作是什么!如何在简历写上熟悉(精通)JVM调优,有过线上调优经验!

    就在前天的时候,马士兵老师讲了一节公开课,分享了涨薪5K的JVM调优骚操作是什么!如何在简历写上熟悉(精通)JVM调优,有过线上调优经验! 主要包含的内容如下: 1. 为什么一个百万级TPS系统会频繁 ...

  4. JVM调优,面到了阿里性能优化师!

    小K 菜哥,我看你朋友圈,你好像换工作了? 菜哥 对啊,前阵子被产品经理烦的头疼,就想换工作了.刚好找到一个不错的. 小K 给我说说呗,让我也参考一下,我现在工资才15K,主管死坑,我也想换工作了 菜 ...

  5. [原创 - 尚学堂科技 - 马士兵老师]

    JAVA自学之路 一:学会选择 [转载请注明出处:http://www.bjsxt.com/zixue/zixuezhilu_1.html] 为了就业,不少同学参加各种各样的培训. 决心做软件的,大多 ...

  6. Java面试必问JVM调优,那.NET5呢?

    JVM调优已经是普通Java工程师的必修课了,而.NET开源快5年了,CLR层面的优化到目前都不多见,甚至常用的性能调优工具都还没玩过..NET5马上来了,要想在互联网大潮中逆袭,光靠平台是不够的,开 ...

  7. 面试又栽在JVM调优上了!

    很多人都是抱着旁观者的心态看关于jvm的文章,给个镜子看看你们的嘴脸- 那我今天要来和你们过过招了,前几天我在知乎上看到一个叫做为什么要学习jvm的话题下回答虽然寥寥无几,却成了大型打脸现场... 想 ...

  8. Java高并发编程 (马士兵老师视频)笔记(一)同步器

    本篇主要总结同步器的相关例子:包括synchronized.volatile.原子变量类(AtomicXxx).CountDownLatch.ReentrantLock和ThreadLocal.还涉及 ...

  9. [转]尚学堂科技 - 马士兵老师-JAVA自学之路

    [原创 - 尚学堂科技 - 马士兵老师] JAVA自学之路 一:学会选择 [转载请注明出处:http://www.bjsxt.com/zixue/zixuezhilu_1.html] 为了就业,不少同 ...

最新文章

  1. modin pandas 加速
  2. 解决Missing artifact com.microsoft.sqlserver:sqljdbc4:jar:4.0问题
  3. angular5 ng-content使用方法
  4. docker-compose配置redis服务
  5. python 数据分析里axis=0/1 行列定义为什么每次都不同?(比如pandas, numpy, DataFrame)
  6. Python 的文件IO相关操作说明
  7. ubuntu 安装 php
  8. 数学,原来可以这么美!
  9. zigbee的路由器能分配网络地址吗_家用无线路由器讲解
  10. 机器学习数学基础篇一:高数基础
  11. JavaEE-面试-Solr面试回答思路
  12. Zune软件教程 Windows Phone同步攻略
  13. 植物大战僵尸变态辅助开发系列教程(E语言实现和VC6实现)(中)
  14. 亚马逊Rating和Review的这些区别你知多少?
  15. 中南大学邮箱客户端手动配置
  16. codeforces每日5题(均1500)-第八天
  17. Python 自动化办公
  18. 计算机吉祥如意制作贺卡作业,贺卡制作教案
  19. VUE项目SEO问题的解决
  20. k均值聚类算法python_K均值和其他聚类算法:Python快速入门

热门文章

  1. Linux下终端字体颜色设置方法
  2. Android平台的Python-基础篇(一)-详细版
  3. raspberry pi_前5名:轻量级电子邮件客户端,教育方面的Raspberry Pi,网络中立性等等
  4. Oracle 数据库一体机的崛起
  5. Android Transform API 从原理到实战
  6. 宇文成 C语言中求三个值中的最大值
  7. zephyr笔记 2.2.2 定时器
  8. Mac 上通过U盘安装Windows 7
  9. SwiftUI 动画教程之如何使用 Animatable实现摇动动画
  10. 2021护理正高考试成绩查询,查分啦:2021年护士执业资格考试成绩正式公布