线上JVM 优化调试(1)-基础知识
1 数据类型
Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。
基本类型包括:byte、short、int、long、char、float、double、Boolean、returnAddress
引用类型包括:类类型、接口类型、数组
2 堆和栈
堆和栈是程序运行的关键,很有必要把他们的关系说清楚。
1)栈是运行时的单位,而堆是存储的单位。
2)栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
在Java中一个线程就会相应有一个线程栈与之对应,这点很容易理解,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享 的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。
3 引用类型
对象引用类型分为:强引用、软引用、弱引用、虚引用
强引用:就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收
软引用:软 引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张, 则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。
弱引用:弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。
强引用不用说,我们系统一般在使用时都是用的强引用。而“软引用”和“弱引用”比较少见。他们一般被作为缓存使用,而且一般是在内存大小比较受限的情况 下做为缓存。因为如果内存足够大的话,可以直接使用强引用作为缓存即可,同时可控性更高。因而,他们常见的是被使用在桌面应用系统的缓存。
4 内存模型
1.线程栈:线程创建是会为每个线程创建一个线程栈,线程栈里面会为每个方法调用创建一个栈帧。主要用于保存线程的当前运行状态。
2.堆:用于存放运行时中生成的新对像。会划分成新生代和老年代。新生代里面又划分成了eden区、存活1区和存活2区。
3.永久区:方法和常量区,用于存放方法字节码元数据和各种常量。
对于大部分应用,常驻对象不多。因为大部分存活寿命不长,新生代和老年代的划分有利于区分对待和缩小垃圾回收范围。
虚拟机中的共划分为三个代:年轻代(Young Generation)、年老点(Old Generation)、持久代(Permanent Generation)
其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。
(1)年轻代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制 过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时 存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空 的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
(2)年老代:
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
(3)持久代:
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设 置。
5 内存回收策略和常见概念
5.1 串行&并行
串行:单线程执行内存回收工作。十分简单,无需考虑同步等问题,但耗时较长,不适合多cpu。
并行:多线程并发进行回收工作。适合多CPU,效率高。
5.2 并发& stop the world
stop the world:jvm里的应用线程会挂起,只有垃圾回收线程在工作进行垃圾清理工作。简单,无需考虑回收不干净等问题。
并发:在垃圾回收的同时,应用也在跑。保证应用的响应时间。会存在回收不干净需要二次回收的情况。
5.3 压缩&非压缩©
压缩:在进行垃圾回收后,会通过滑动,把存活对象滑动到连续的空间里,清理碎片,保证剩余的空间是连续的。
非压缩:保留碎片,不进行压缩。
copy:将存活对象移到新空间,老空间全部释放。(需要较大的内存。)
一个垃圾回收算法,可以从上面几个维度来考虑和设计,而最终产生拥有不同特性适合不同场景的垃圾回收器。
2.HotSpot JVM的YGC&FGC
YGC :对新生代堆进行GC。频率比较高,因为大部分对象的存活寿命较短,在新生代里被回收。性能耗费较小。
FGC :全堆范围的GC。默认堆空间使用到达80%(可调整)的时候会触发FGC。以我们生产环境为例,一般比较少会触发FGC,有时10天或一周左右会有一次。
二、垃圾回收算法
1)按照基本回收策略分
(1)引用计数(Reference Counting):
比较古老的回收算法。原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数。垃圾回收时,引用收集计数为0的对象。此算法最致命的是无法处理循环引用的问题。
(2)标记-清除(Mark-Sweep):
此算法执行分两阶段。第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法需要暂停整个应用,同时,会产生内存碎片。
(3)复制(Copying):
此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处 理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。当然,此算法的缺点也是很明显的,就是需要两 倍内存空间。
(4)标记-整理(Mark-Compact):
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并 且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
2)按分区对待的方式分
增量收集(Incremental Collecting):实时垃圾回收算法,即:在应用进行的同时进行垃圾回收。不知道什么原因JDK5.0中的收集器没有使用这种算法的。
分代收集(Generational Collecting):基于对对象生命周期分析后得出的垃圾回收算法。把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。
3)按系统线程分
(1)串行收集:串行收集使用单线程处理所有垃圾回收工作,因为无需多线程交互,实现容易,而且效率比较高。但是,其局限性也比较明显,即无法使用多处理器的优势,所以此收集适合单处理器机器。当然,此收集器也可以用在小数据量(100M左右)情况下的多处理器机器上。
(2)并行收集:并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU数目越多,越能体现出并行收集器的优势。
(3)并发收集:相对于串行收集和并行收集而言,前面两个在进行垃圾回收工作时,需要暂停整个运行环境,而只有垃圾回收程序在运行,因此,系统在垃圾回收时会有明显的暂停,而且暂停时间会因为堆越大而越长。
触发垃圾回收
由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:YGC和FGC
(1)YGC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发YGC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对 年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因 而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
(2)FGC
对整个堆进行整理,包括Young、Tenured和Perm。FGC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用
·上一次GC之后Heap的各域分配策略动态变化
serial collector
使用单线程去完成所有的gc工作,没有线程间的通信,这种方式会相对高效
单处理器机器且没有pause time的要求
Client模式下默认:
可使用
强制使用参数:
-XX:+UseSerialGC
优点:对server应用没什么优点
缺点:慢,不能充分发挥硬件资源
YGC
eden空间不足
FGC
old空间不足
perm空间不足
显示调用System.gc(),包括RMI等的定时触发
parallel collector
使用多线程的方式,利用多CUP来提高GC的效率,主要以到达一定的吞吐量为目标适用于科学技术和后台处理
有中规模/大规模数据集大小的应用且运行在多处理器上,关注吞吐量(throughput)
Server模式下默认
--YGC:PS FGC:Parallel MSC
强制使用参数:
-XX:+UseParallelGC或-XX:+UseParallelOldGC
--ParallelGC代表FGC为Parallel MSC
--ParallelOldGC代表FGC为Parallel Compacting
优点:高效
缺点:heap变大后造成暂停时间会变长
YGC
eden空间不足
FGC
old空间不足
perm空间不足
显示调用System.gc(),包括RMI等的定时触发
concurrent collector
使用多线程的方式,利用多CUP来提高GC的效率,并发完成大部分工作,使得gc pause短
适合中大规模数据集的应用,应用服务器,电信领域,关注response time,而不是throughput
可用-XX:+UseConcMarkSweepGC强制指定
优点:
对old进行回收时,对应用造成的暂停时间非常短,适合对latency要求比较高的应用
缺点:
1.内存碎片和浮动垃圾
2.old去的内存分配效率低
3.回收的整个耗时比较长
4.和应用争抢CPU
YGC
eden空间不足
CMS GC
1.old Gen使用率大的比率,默认为92%
2.配置了CMSClassUnloadingEnabled,且Perm Gen的使用达到一定的比率默认为92%
3.Hotspot自己根据估计决定是否要触法
4.在配置了ExplictGCInvokesConcurrent的情况下显示调用了System.gc()
相关推荐
1. **JVM架构**:JVM是Java平台的核心组成部分,它负责执行字节码,提供了一个跨平台的运行环境。JVM包括类装载器、运行时数据区、执行引擎、本地方法接口和本地方法库等主要组件。 2. **类装载机制**:JVM的类装载...
例如,了解Java中的布尔类型实际上是如何在JVM内部被处理的(使用整型的1和0表示),这对于调试特定类型的问题尤为重要。 - **类加载过程**:深入理解类加载器、类路径和类名之间的关系对于确保应用程序正确加载和...
- **`watch`命令**: 监控指定表达式的值变化,常用于调试或性能优化。 - **`jvm`命令**: 查看JVM相关信息,如内存、线程、垃圾收集等,辅助性能调优。 - **`ddd`命令**: 用于诊断数据依赖关系,帮助理解数据流走向。...
Java的JVM(Java Virtual Machine)是Java程序运行的基础,它是一个虚拟的...掌握这些JVM相关知识点,不仅能够帮助Java开发者在面试中脱颖而出,也能在实际开发中更好地调试和优化应用程序,提高软件性能和稳定性。
通过熟练掌握和应用Arthas,开发者可以在遇到各种复杂问题时,更高效地进行故障排查和JVM优化,提升软件系统的稳定性和性能。同时,Arthas的开源特性也意味着它拥有活跃的社区支持,不断迭代更新,以满足更多开发者...
2. **动态代码执行**:通过`asm`库,Arthas支持在运行时对目标类的代码进行修改,这对于调试和优化代码非常有用。例如,你可以添加日志语句、改变变量值或者临时替换方法实现。 3. **类监控**:`trace`命令允许...
SpringBoot远程调试是开发者在处理复杂问题或优化代码时常用的一种技术。它允许开发者在原始开发环境中连接到运行在远程服务器上的应用,实时查看和控制程序执行,从而更有效地定位和解决问题。本压缩包"远程调试...
生产模式则关闭了一些不必要的诊断功能,优化了内存使用,更适合线上环境。 2.1.2 两种模式的不同 开发模式下,服务器会自动重启失败的应用,且类加载器允许热部署;而在生产模式下,这些特性被禁用,以确保服务的...
阿里Arthas,全称为Alibaba Java诊断神器,是一款强大的Java运行时诊断工具,专为解决线上问题、优化JVM、实时查看和调整日志级别以及进行代码在线反编译等问题而设计。自3.4.6版本以来,它以其便捷高效的特点,深受...
总结,BTrace作为一款强大的Java在线调试工具,通过字节码增强技术实现了对运行中的Java应用的实时监控,对于开发人员而言,是进行性能优化和问题排查的得力助手。合理运用BTrace,可以极大地提升线上问题解决的效率...
Arthas旨在帮助开发者在不重启Java应用的情况下,进行问题定位、性能分析、线上调试等操作,极大地提升了开发效率。这个工具在源码分析、毕业设计论文编写、计算机案例研究以及模板建站和系统软件开发过程中都具有很...
1. **命令行工具**:Arthas提供了一套强大的命令行工具集,如`asm`命令用于查看类的信息,`trace`命令追踪方法调用,`watch`命令监控变量或表达式的变化等,这些都极大地便利了开发者在运行时的调试工作。...
1. **定位方法调用异常**:当线上出现异常时,可以使用`trace`命令跟踪异常抛出的方法,记录每次调用的参数和返回值,从而找到问题的源头。 2. **优化性能**:通过`monitor`命令监控关键业务方法的执行时间,找出...
而`jvm`命令则能展示JVM的详细信息,如垃圾收集状态、类加载器等,有助于分析和优化JVM配置。此外,对于Spring应用,`scan`命令能列出所有已加载的Spring Bean及其相关信息。 三、拆箱即用 Arthas的设计理念之一...
2. **AOP(面向切面编程)支持**:Arthas能够在线添加AOP切面,对运行中的代码进行拦截,这对于调试和优化非常有用。 3. **Classloader Debug**:由于Java的类加载机制可能导致一些问题,Arthas提供了强大的类加载...
1. **命令行工具集**:Arthas提供了一系列命令行工具,如`asm`用于查看类的信息,`sc`用于查看运行时的类和方法,`tdump`用于追踪指定方法的调用堆栈,`jvm`则可以展示JVM的相关信息。 2. **动态代码执行**:`expr`...
同时,了解如何进行远程调试也是每个Java Web开发者必备的技能之一,它不仅可以应用于本地环境,还能在生产环境中实现无侵入式的线上调试。在实际开发过程中,你可能还需要关注日志配置、错误处理、性能优化等相关...
例如,当遇到某个方法执行有问题时,可以通过Arthas动态插入调试代码或替换原有的类文件,实时观察方法执行过程,无需繁琐的重新编译和部署流程。 其次,Arthas提供了全面的JVM监控能力。通过命令行界面,开发者...
10. **jvm.html**: Arthas支持对JVM进行深入操作,如查看内存、类加载器、垃圾收集等,这个文档可能详细解释了如何使用Arthas来监控和优化JVM。 通过这些离线文档,开发者不仅可以了解Arthas的各个功能,还能学习...