JVM原理和调优

本文是学习时的自我总结,用于日后温习。如有错误还望谅解,不吝赐教

此处附上本文出处:http://blog.csdn.net/hjxgood/article/details/53896229

一、什么是JVM

JVM是一个虚构出来的计算机,实现了编程语言(如Java、Scala)的与平台的无关性

二、Java代码编译和执行过程

1.   Java代码编译和执行包含的三个重要机制:

  1)   Java源码编译机制

  2)   类加载机制

  3)   类执行机制

2.   Java代码编译是由Java源码编译器来完成,由源代码编译为JVM字节码

3.   Java字节码的执行是由JVM执行引擎来完成,由JVM字节码编译为目标代码

1.Java源码编译机制由以下三个过程组成:

  • 分析和输入到符号表
  • 注解处理
  • 语义分析和生成class文件

最后生成的class文件由以下部分组成:

  • 结构信息。包括class文件格式版本号及各部分的数量与大小的信息
  • 元数据。对应于Java源码中声明与常量的信息。包含类/继承的超类/实现的接口的声明信息、域与方法声明信息和常量池
  • 方法信息。对应Java源码中语句和表达式对应的信息。包含字节码、异常处理器表、求值栈与局部变量区大小、求值栈的类型记录、调试符号信息

2.类加载机制。JVM的类加载是通过ClassLoader及其子类完成的

1)Bootstrap ClassLoader

负责加载JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

2)Extension ClassLoader

负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包

3)App ClassLoader

负责加载classpath中指定的jar包及目录中class

4)Custom ClassLoader

属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap ClassLoader逐层检查,保证此类只所有ClassLoader加载一次。

加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。

3.类执行机制

1)   JVM是基于栈的体系结构来执行class字节码的。

2)   线程创建后,都会产生程序计数器(PC)和栈(Stack),程序计数器存放下一条要执行的指令在方法内的偏移量,栈中存放一个个栈帧,每个栈帧对应着每个方法的每次调用。

3)   栈帧是由局部变量区和操作数栈两部分组成,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法执行过程中产生的中间结果。

三、JVM内存管理

JVM由堆、栈、本地方法栈、方法区等部分组成

1.堆

  所有通过new创建的对象的内存都在堆中分配,堆的大小可以通过-Xmx和-Xms来控制。

  堆被划分为新生代和旧生代,新生代又被分为Eden和Survivor区,Survivor由From Space和To Space组成

  • 新生代。新建的对象都是用新生代分配内存,Eden空间不足时,会把存活的对象转移到Survivor中
  • 旧生代。用于存放新生代中经过多次垃圾回收仍然存活的对象
  • 持久带。主要存放所有已加载的类信息,方法信息,常量池等等

2.栈

  每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果。

  -xss:设置每个线程的堆栈大小。JDK1.5+ 每个线程堆栈大小为1M,一般来说如果栈不是很深的话,1M 是够用的

3.本地方法栈

  用于支持native方法的执行,存储了每个native方法调用的状态

4.方法区

  存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。

四、垃圾回收

1.垃圾回收基本策略

  1)   引用计数:

  较古老的回收算法。原理是此对象有一个引用即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,只用手机计数为0的对象。此算法最致命的是无法处理循环引用的问题

  2)   标记清除:

  此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时会产生内存碎片

  3)   复制:

  此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。

垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两倍内存空间

  4)   标记-整理(Mark-Compact):

  结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,清除未标记对象 并且 把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题

2.新生代的GC:

新生代通常存活时间较短,因此基于 复制算法 来进行回收,即扫描出存活的对象,并复制到一块新的完全未使用的空间中。

新生代采用空闲指针的方式来控制GC触发指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代。

3.旧生代的GC:

旧生代对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,即扫描出存活的对象,然后回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。

4.  在执行机制上JVM提供了串行GC(Serial GC)、并行回收GC(Parallel Scavenge)和并行GC(ParNew)

1)串行GC

在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式

2)并行回收GC

在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式

3)并行GC

与旧生代的并发GC配合使用

五、JVM内存调优

对JVM内存调优的时候不能只看操作系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用情况,因为GC过后这个值是不会变化的,因此内存调优的时候要更多地使用JDK提供的内存查看工具

对JVM内存的系统级的调优主要的目的是减少GC的频率和Full GC的次数,过多的GC和Full GC是会占用很多的系统资源(主要是CPU),影响系统的吞吐量。特别要关注Full GC,因为它会对整个堆进行整理

1.导致Full GC一般由于以下几种情况:

1)   旧生代空间不足

调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在旧生代创建对象 

2)  
Pemanet
Generation空间不足

增大Perm Gen空间,避免太多静态对象

统计得到的GC后晋升到旧生代的平均大小大于旧生代剩余空间

控制好新生代和旧生代的比例

3)   System.gc()被显示调用

垃圾回收不要手动触发,尽量依靠JVM自身的机制

2.调优手段主要通过控制堆内存的各个部分的比例和GC策略来实现,下面是各部分比例不良设置会导致的后果

1)  
新生代设置过小

新生代GC次数非常频繁,增大系统消耗;导致大对象直接进入旧生代,占据了旧生代剩余空间,诱发Full GC

2)  
新生代设置过大

新生代GC耗时大幅度增加;导致旧生代过小(堆总量一定),从而诱发Full GC

一般说来新生代占整个堆1/3比较合适

3)  
Survivor设置过小

导致对象从eden直接到达旧生代,降低了在新生代的存活时间

4)  
Survivor设置过大

导致eden过小,增加了GC频率

3.JVM提供两种较为简单的GC策略的设置方式

1)吞吐量优先。JVM以吞吐量为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,来达到吞吐量指标。

2)暂停时间优先。JVM以暂停时间为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,尽量保证每次GC造成的应用停止时间都在指定的数值范围内完成。

时间: 2024-08-01 17:25:18

JVM原理和调优的相关文章

JVM原理及调优--网页链接收藏

此篇用于收藏大神们关于JVM原理及调优通俗易懂的文章链接,用于随时查看 JVM调优总结 JVM参数配置大全 JVM调优:选择合适的GC collector

[大数据性能调优] 第一章:性能调优的本质、Spark资源使用原理和调优要点分析

本課主題 大数据性能调优的本质 Spark 性能调优要点分析 Spark 资源使用原理流程 Spark 资源调优最佳实战 Spark 更高性能的算子 引言 我们谈大数据性能调优,到底在谈什么,它的本质是什么,以及 Spark 在性能调优部份的要点,这两点让直式进入性能调优都是一个至关重要的问题,它的本质限制了我们调优到底要达到一个什么样的目标或者说我们是从什么本源上进行调优.希望这篇文章能为读者带出以下的启发: 了解大数据性能调优的本质 了解 Spark 性能调优要点分析 了解 Spark 在资

一文读懂Java GC原理和调优

概述 本文介绍GC基础原理和理论,GC调优方法思路和方法,基于Hotspot jdk1.8,学习之后将了解如何对生产系统出现的GC问题进行排查解决 阅读时长约30分钟,内容主要如下: GC基础原理,涉及调优目标,GC事件分类.JVM内存分配策略.GC日志分析等 CMS原理及调优 G1原理及调优 GC问题排查和解决思路 GC基础原理 1 GC调优目标 大多数情况下对 Java 程序进行GC调优, 主要关注两个目标:响应速度.吞吐量 响应速度(Responsiveness) 响应速度指程序或系统对一

2020年薪30W的Java程序员都要求熟悉JVM与性能调优!

前言 作为Java程序员,你有没有被JVM伤害过?面试的时候是否碰到过对JVM的灵魂拷问? 一.JVM 内存区域划分 1.程序计数器(线程私有) 程序计数器(Program Counter Register),也有称作为 PC 寄存器.保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当 CPU 需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加 1 或者根据转移指针得到下一条

Tomcat(JVM)性能调优

Tomcat架构图 Tomcat与JVM版本优化Tomcat的运行是基于Java的虚拟机.SUN的JVM动态库有client和server两个版本,分别针对桌面应用和服务器应用做了相应的优化,client版本加载速度较快,server版本加载速度较慢但运行起来较快.在命令行输入 java -version 可以看到jvm目前配置的是哪个版本.如果要修改jvm的版本,可更改默认java.exe调用的jvm.dll,这个由jvm.cfg决定.编辑%JAVA_HOME%/jre/lib/i386/jv

JVM常用的调优参数

-Xms512m -Xmx1024m -XX:MaxPermSize=1204m -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled 上面是一般性调优测试,设置Java JVM整体的优化参数: set JAVA_OPTS="-Xms256m -Xmx512m -XX:MaxPermSize=1204m XX:+CMSClassUnloadingEnabled -XX:+CMSPe

Java系列笔记 - JVM监控与调优

光说不练假把式,学习Java GC机制的目的是为了实用,也就是为了在JVM出现问题时分析原因并解决之.通过学习,我觉得JVM监控与调优主要的着眼点在于如何配置.如何监控.如何优化3点上.下面就将针对这3点进行学习. (如果您对Java的内存区域划分和内存回收机制尚不明确,那在阅读本文前,请先阅读我的前一篇博客<Java系列笔记(3) - Java 内存区域和GC机制>,在该博客中,详细叙述了Java HotSpot虚拟机(Sun/Oracle JDK系列默认的虚拟机)的内存分配和垃圾回收机制.

JVM层GC调优(下)

GC日志格式 本文是 JVM层GC调优(上) 的后续,在上一篇文章中,我们介绍了JVM的内存结构.常见的垃圾回收算法以及垃圾收集器和不同收集器中的一些GC调优参数.所以通过上文,我们也对GC相关的内容有了一定的了解. 但是光知道一些调优参数是没用的,我们需要能够从GC的日志中去分析可以调优的地方,这样才能使用这些参数去进行相应的调整,所以本小节将介绍一下不同收集器的GC日志格式. 我们这里以Tomcat为例,首先需要在Tomcat的catalina.sh脚本文件中加入打印GC日志的相关参数,如下

JVM监控和调优常用命令工具总结

JVM监控和调优 在Java应用和服务出现莫名的卡顿.CPU飙升等问题时总是要分析一下对应进程的JVM状态以定位问题和解决问题并作出相应的优化,在这过程中Java自带的一些状态监控命令和图形化工具就非常方便了.本文总结了最常用的命令行工具及其常用参数解释,图形化监控工具的用法,仅供参考. jps Java版的ps命令,查看java进程及其相关的信息,如果你想找到一个java进程的pid,那可以用jps命令替代linux中的ps命令了,简单而方便. 命令格式: jps [options] [hos