大道至简 少字全意 易经的方式看 jvm 内存模型 、 内存异常、内存调优实战案例 、类加载机制、双亲委派模型 内存泄漏分析:场景 、 jvm基础 、gc、日志解读实战、GC垃圾回收算法及应用

目录

介绍

 jvm内存模型

一、线程私有区域

 二、线程共享区域

1.堆Heap

2. 方法区Method Area

3.运行时常量池 Runtime constant Pool

三、直接内存(Direct Memory)

四、内存异常与调优

五、总结对比

类加载机制

一、类加载的三大阶段 

二、双亲委派模型 

三、类加载的特殊场景 

四、注意事项 

****双亲委派模型****

一、什么事双亲委派模型

二、类加载器的层级结构

三、工作流程(以加载一个用户自定义类为例)

四、核心有点

五、通俗比喻

*自定义类加载器*

1.为什么需要

2.实现原理

3.简单代码逻辑

典型场景

注意事项

***JVM 性能调优 案例 实战***

一、调优核心目标 (平衡三要素)

二、调优工具 (快速定位问题)

三、调优步骤(六步法)

四、常见问题与解决

五、通俗比喻理解

六、注意事项

***java内存泄漏分析:场景及方法***

一、常见内存泄漏场景

二、内存泄漏分析方法

三、总结与规避建议

**GC日志解读 实战 案例**

一、GC日志核心字段

二、常见问题排查方法

三、工具辅助分析

四、实战案例

***GC垃圾回收算法及应用案例***

一、核心垃圾回收算法

二、应用案例与典型场景

三、配置与使用指南

总结

jvm基础

一、基本定义与作用

二、核心架构组成

三、运行机制

1.类加载流程

2.线程执行模型

3.跨平台实现

四、核心特点

五、常见JVM 实现


介绍

         jvm基础 、 内存模型 、 gc、 内存异常、内存调优实战案例 、类加载机制、双亲委派模型、 文章上下文 没有强关联性 根据需要读章节即可

 jvm内存模型

一、线程私有区域

1. 程序计数器(Program Counter Register)
        每个线程独立拥有,记录当前线程执行字节码的指令地
        唯一不会出现 outofMemoryError的区域,生命周期与线程一致。
2.虚拟机栈 (JVM Stack)
        用于Java方法执行,每个方法对应一个栈顿 (Stack Frame) ,存储局部变量表、操作数栈、动态链接、方法出口等信息
        局部变量表大小在编译期确定,栈深度不足会抛出 StackOverfilowError;无法扩展时触发 outofMemoryError
3本地方法栈(Native Method Stack)
        与虚拟机栈类似,但服务于Native方法 (如C/C++实现的方法) 
        部分JVM实现 (如HotSpot) 将虚拟机栈与本地方法栈合并 

 二、线程共享区域

1.堆Heap

  • 最大内存区域,存放对象实例及数组,分为新生代 (Young Generation)和老年代 (Old Generation)
  • 新生代: 包含Eden区和两个Survivor区(SO/S1),对象优先分配在Eden,Minor GC后存活对象进入Survivor,多次存活后晋升老年代 。
  • 老年代:存放长期存活对象,Major GC (Full GC) 触发时回收 。
  • 参数控制:-xms (初始堆大小) 、-xmx (最大堆大小)。

2. 方法区Method Area

  • 存储类元数据 (类名、字段、方法》 、常量、静态变量、即时编译器 (JIT) 生成的代码 0 3
  • JDK演变:
    • JDK7及之前:通过永久代(PermGen) 实现,受-xx:PermSize和-xx:MaxPermSize控制。
    • JDK8+:改用元空间 (Metaspace)使用本地内存,大小由-xX:MaxMetaspaceSize 限制

3.运行时常量池 Runtime constant Pool

  • ·方法区的一部分,存储编译期生成的字面量(如字符串)和符号引用,支持运行期间动态添加(如 String.intem0) 

三、直接内存(Direct Memory)

  • 方法区的一部分,存储编译期生成的字面量(如字符串)和符号引用,支持运行期间动态添加(如  Strina.intern() )

四、内存异常调优

1.常见异常

  • outofMemoryError:堆、方法区、直接内存不足时触发
  • StackOverflowError :栈深度超过限制时触发

2.调优参数示例

  • -xX:MaxTenuringThreshold :控制对象晋升老年代的年龄
  •  -XX:+UseAdaptiveSizePolicy :动态调整堆各区域比例

五、总结对比

类加载机制


 Java类加载机制是JVM将.class文件加载到内存并生成可执行类的核心过程

一、类加载的三大阶段 

1. 加载(Loading)
   - 通过类的全限定名获取二进制字节流,将静态存储结构转化为方法区的运行时数据结构,并在堆中生成对应的`java.lang.Class`对象作为访问入口 
   - 数据来源包括本地文件、JAR包、网络资源、动态代理生成等 
2. 连接(Linking)
   - 验证:确保字节流符合规范,包括文件格式验证(魔数、版本号等)、元数据验证(语义正确性)、字节码验证(逻辑合法性)、符号引用验证(解析可行性) 
   - 准备:为类变量(`static`修饰)分配内存并设置默认值(如`int`初始化为0),`final static`变量直接赋代码中定义的值 
   - 解析:将常量池中的符号引用转换为直接引用(如内存地址偏移量) 
3. 初始化(Initialization)
   - 执行类构造器`<clinit>()`方法,为静态变量显式赋值并执行静态代码块 
   - 初始化触发条件包括:`new`实例、调用静态方法/字段、反射加载类、初始化子类、启动主类等 

二、双亲委派模型 

. 1.类加载器层级 
   - Bootstrap ClassLoader:加载`%JAVA_HOME%/lib`核心类(如`java.lang.*`),由C++实现 
   - Platform/Extension ClassLoader(Java 9+):加载平台扩展模块或`%JRE_HOME%/ext`目录的类 
   - Application ClassLoader:加载用户类路径(`-classpath`)的类 
 2. 委派机制
   - 类加载请求优先委派父加载器处理,若父类无法加载(如搜索路径中不存在),子类才尝试加载 
   - 优点:避免重复加载核心类,保证安全性(如防止自定义`java.lang.String`破坏核心功能) 

三、类加载的特殊场景 

1. 自定义类加载器
   - 继承`ClassLoader`并重写`findClass()`方法,用于加载加密类、热部署、模块化隔离等场景 
   - 打破双亲委派:如Tomcat为每个Web应用单独使用`WebappClassLoader`,实现类隔离 
2. Java 9+的调整
   - 引入模块化系统,将扩展类加载器重命名为平台类加载器(`PlatformClassLoader`),支持模块化依赖管理 

四、注意事项 

1. 主动使用与被动引用
   - 访问父类静态字段不会触发子类初始化,但通过数组定义类(如`MyClass[] arr`)不会触发初始化 
2. 类卸载条件
   - 类的所有实例被回收,且对应的`Class`对象无引用,同时加载该类的`ClassLoader`被回收 
 
通过理解类加载机制,开发者能优化内存管理、实现动态扩展(如插件系统)、规避类冲突问题  

****双亲委派模型****

简单理解先找爹再自己干

一、什么事双亲委派模型

        当类加载器收到类加载请求时,会先将任务向上委派给父类加载器处理。只有当父类加载器无法完成时,子类加载器才会自己尝试加载,这种机制形成了自下而上的层级委托链,最终由顶层的启动类加载器(Botstrap ClassLoader)作为兜底 

二、类加载器的层级结构

1.启动类加载器 (Bootstrap ClassLoader
        由 C++实现,负责加载 JAVA_HOME/ib 下的核心类库(如 jar) ,是唯一没有父类的加载器 。
2.扩展类加载器(Extension ClassLoader
        加载 JAVA HOME/ib/ext 目录下的扩展类(如JDK 的扩展功能)
3.应用程序类加载器(Application ClassLoader)
        加载用户类路径 (ClassPath) 下的类,如项目中的 .class 文件和第三方 Jar 包

三、工作流程(以加载一个用户自定义类为例)

1.应用程序类加载器收到请求,先检查是否已加载过该类
2.向上委派:将请求传递给扩展类加载器
3.扩展类加载器继续向上委派给启动类加载器
4.启动类加载器尝试加载:若成功则返回结果;若失败(如核心库中没有该类),则逐层向下通知子加载器尝试加载
5.最终由应用程序类加载器加载用户类,并缓存结果 
 

四、核心有点

1.安全性:防止核心类库(如 java.lang.string)被篡改。例如,用户自定义的同名类不会被加载,因为父加载器已优先加载了 JDK的核心类
2.避免重复加载: 每个类只加载一次,由父加载器缓存结果,子加载器无需重复处理
3.类隔离性:不同层级的加载器加载的类相互隔离,避免冲突(如 Tomcat 通过自定义加载器隔离不同 Web 应用) 
 

五、通俗比喻

想象一家公司处理问题的方式

  • 基层员工 (应用类加载器)遇到问题先向上级汇报
  • 中层领导 (扩展类加载器):若解决不了,继续上报给 CEO
  • CEO (启动类加载器): 处理重大问题,若无法解决,再逐级向下指派

这种机制保证了核心决策由高层把控,同时基层也能处理个性化需求
通过这种机制,Java 既保证了核心类库的安全,又实现了灵活的动态加载
 

*自定义类加载器*

1.为什么需要

  • 加密保护:防止别人反编译你的代码。例如,将 .cass 文件加密后,只有你的加载器能解密使用
  • 非标准来源:比如从网络、数据库加载类,而不是传统的本地文件
  • 热更新:不重启程序就能替换已加载的类 

2.实现原理

  • 继承 classLoader 类,重写 findClass() 方法 (而不是直接改 loadClass() ,避免破坏双亲委派机制)
  • 在findclass0 中读取类文件的二进制数据(如解密文件或网络请求),调用 defineClass 将其转为 JVM 可识别的类
     

3.简单代码逻辑

class MyLoader extends ClassLoader {
    @Override 
    protected Class<?> findClass(String name) {
        // 1. 从加密文件/网络等读取二进制数据 
        byte[] bytes = loadClassData(name); 
        // 2. 解密或处理数据(如果需要)
        // 3. 将数据转为类对象 
        return defineClass(name, bytes, 0, bytes.length);
    }
}

典型场景

  • 游戏插件:动态加载新功能模块
  • 企业应用:不同模块用不同加载器隔离,避免类冲突,
  • 代码保护:防止核心代码被反编译


注意事项

  • 不要随便破坏双亲委派: 默认机制能保证核心类安全,自定义时尽量只扩展不推翻 
  • 类隔离:同一个类被不同加载器加载会被视为不同的类 (可能导致类型转换异常)
     

***JVM 性能调优 案例 实战***

        JVM性能调优的核心目标是让 Java程序在合理的内存占用下,减少卡顿 (GC停顿时间)并提高运行效率(吞叶量)

一、调优核心目标 (平衡三要素)

1.内存占用:程序运行时需要的内存大小
        堆内存太大可能导致GC时间变长太小可能频繁触发GC甚至溢出

2.延迟:垃圾回收导致的程序卡顿时间
        例如Full GC会暂停所有线程,严重影响用户体验

3.吞吐量:程序运行时间占总时间的比例
        高吞吐量意味着程序大部分时间在处理业务而非垃圾回收

二、调优工具 (快速定位问题)

三、调优步骤(六步法)

1.监控问题
通过GC日志(-xloggc:gc.log) 和工具观察:

  • 是否频繁Full GC?
  • Young区对象是否过快晋升到 old区?
  • 内存泄漏迹象 (如老年代占用持续增长)

2.调整参数

  • 堆内存:-Xmx4g -Xms4g(最大和初始堆设为相同值,避免动态调整开销)
  • 年轻代:-Xmn2g(年轻代大小,建议占堆的1/3~1/2)
  • GC算法: 低延迟选G1 (-xx:+UseG1gc),高吞吐选Parallel

3.验证效果
对比调整前后的GC频率、停顿时间 (如原Full GC每次5秒>优化后1秒)
 

四、常见问题与解决

五、通俗比喻理解

堆内存:像房间,年轻代临时储物间(频繁清理),老年代 长期仓库 (很少清理)

GC调优: 调中房间布局,让垃圾 无用对象 即使清理掉,避免房间堵塞 大扫除 Full GC 耗时

内存泄漏: 像忘关水龙头 水(内存)不断累积,最终益处池子

六、注意事项

1.避免优化过早:先通过日志工具  确认瓶颈再调整

2.参数逐步调整:每次只改1 2个参数 观察效果

3.关注业务场景: 高并发系统 优先 降低延迟离线计算机系统优先提高吞吐

***java内存泄漏分析:场景及方法***


        以生活中“垃圾没扔进垃圾桶"类比,通俗理解内存泄漏:程序中有对象  不再使用,但因被错误引用  无法被回收,最终导致内存资源耗尽

一、常见内存泄漏场景

1.静态集合长期持有对象引用

  • 静态容器(如 HashMap、ArayList)的生命周期与程序一致,若入大量临时对象且及时清理,会导致无用对象无法释放
  • 示例:全局缓存系统未清过期数据

2.修改集合元素的哈希值 (HashSet/HashMap)

  • 若修改 Hashset 中元素的hashcode 或 equas相关字段,会导致元素在哈希表中的存储位置变化,但位置仍被引用,无法通过remove()) 正确删除
  • 解决方法: 修改已存入集合的对象的哈希相关字段,或重写hashcodel 和 equals 时保持逻辑稳定

3.单例模式持有外部对象

  • 单例对象储在方法区 (永久代),用了生命周期对象 (如临时数据),会导致这些对象无法被回收
  • 示例:单例类中缓存用户请求数据设置  清理策略。

4.未关闭的资源连接

  • 数据库连接、网络连接(Socket) 、I0流等未调用 close0 显式关闭,即使对象不再使用,连接仍占用内存 
  • 解决方法:在finally代码块中关闭资源,或使用 try-with-resources语法

5.监听器与内部类引用

  • 添加监听器(如 addxxxListener())后未移除,或非静态内部类隐式持有外部类引用,导致外部类无法回收 
  • 示例:Activity中注册未取消的广播监听
     

二、内存泄漏分析方法


1.使用工具检测

  • Java自带工具: 通过jmap生成堆转储文件(Heap Dump),用jhat或VisuaNM分析对象引用链,定位未被回收的对象 
  • ·第三方工具: Eclipse Memory Analyzer (MAT) 、JProfiler等可视化工具,可快速定位泄漏点 

2.代码审查关键点

  • 检查长生命周期对象 (如静态变量、单例)是否引用了短生命周期对象。
  • 确认集合类 (如 Vector 、HashMap) 使用后是否清空或置为 null 。
  • 验证 equals0 和 hashCode0)重写是否符合规范 (避免集合操作异常) 

3.模拟压力测试

  • 长时间运行程序或重复执行特定操作,观察内存占用是否持续增长 (如 outofMemoryError提示) 

三、总结与规避建议

  • 核心原则:确保无用对象不被长生命周期对象引用
  • 编码习惯:及时释放资源、避免静态集合滥用、谨慎使用单例.
  • 工具辅助:结合 VisualVM 监控内存变化:,定期进行堆转储分析

**GC日志解读 实战 案例**

一、GC日志核心字段

1.时间相关指标
        时间戳:如 5.141:Gc pause 表示JVM启动后5.141秒发生GC。
        新停时间: Total time for which application threads were stopped: 0.0782 seconds 表示STW (全局停顿)总耗时
2.内存变化
        各区域容量:如 Eden区 (年轻代) >Survivor区>老年代 的内存占用变化
        回收效果:Freed 1024K(10%) 表示本次回收释放的内存比例
3.GC类型
        Minor GC:回收年轻代,日志中通常标记为 gc 或 Young gc
        Full GC:回收整个堆,日志中明确标注 Full gc,需重点关注其频率和耗时

二、常见问题排查方法

场景1:频繁Full GC
        日志特征: 短时间内多次出现 Full gc,且老年代回收后内存无明显释放
        可能原因:

  •         内存泄漏:对象无法回收,老年代逐渐占满。通过jmap -histo 分析对象分布
    • 大对象分配:直接进入老年代,触发Full GC

场景2:年轻代回收效率低
        日志特征:Minor gc 后存活对象过多,频繁晋升到老年代 (如Premature Promotion)
        解决方案:
                调整-xx:SurvivorRatio 增加 Survivor 区比例
                检查对象生命周期,避免短期对象长期存活,
场景3:吞吐量不足
        日志特征:Throughput(应用运行时间占比)低于90%
        优化方向:
                增加堆内存(-xmx)
                更换GC算法 (如G1优化停顿时间)

三、工具辅助分析

1.GCViewer (本地工具)
        可视化展示内存变化、GC频率、吞吐量等 。
        支持对比不同GC日志,快速定位异常时段。
2.GCeasy (在线平台
        上传日志自动生成报告,标注潜在问题 (如内存泄漏、长暂停)
3.JVM内置命令
        jstat -gcutil实时监控各区域使用率
        jmap -dump生成堆快照,结合MAT分析对象引用链

四、实战案例

问题描述:应用运行一段时间后卡顿,日志显示 Full Gc 耗时1秒以上。
分析步骤:
        1.用GCViewer打开日志,发现Full GC频率每小时超过5次
        2. jmap -histo发现某个缓存类实例数异常增长
        3.检查代码发现缓存未设置过期策略,修复后Full GC频率降至每天1次

***GC垃圾回收算法及应用案例***

一、核心垃圾回收算法

1.标记-清除算法(Mark-Sweep)
        原理:分为标记清除两阶段。标记阶段通过可达性分析识别存活对象清除阶段回收未被标记的对象。
        缺点:产生内存碎片,影响大对象分配效率;需两次扫描,效率较低 
        应用案例: 老年代垃圾回收 (如 CMS收集器的初始标记和并发标记阶段)
2.复制算法(Copying)
        原理:将内存分为两块(如Eden和Survivor区),每次仅使用一块。垃圾回收时,将存活对象复制到另一块内存,清空原区域。
        优点:无内存碎片,适合对象存活率低的场景
        应用案例: 新生代垃圾回收 (YGC/Minor GC) ,如Serial、ParNew收集器 

3.标记整理算法 (Mark-Compact)
        原理:标记存活对象后,将其向内存一端移动清除边界外的空间。
        优点:避免内存碎片,适合对象存活率高的场景。
        应用案例:老年代垃圾回收 (如Serial Old、Parallel Old收集器) 
4.分代收集算法 (Generational Collection)
        原理:将堆分为新生代和老年代,针对不同区域特性选择算法:
        新生代:存活率低,采用复制算法 (如Eden区到Survivor区)
        老年代:存活率高,采用标记-清除或标记-整理算法。
        应用案例: HotSpot虚拟机默认策略,如Parallel Scavenge (新生代) 与Parallel Old (老年代)组合 

二、应用案例与典型场景

1.CMS收集器 (Concurrent Mark Sweep)

  • 算法:基于标记-清除,通过并发标记减少停顿时间
  • 场景: 适用于对延迟敏感的老年代回收 (如Web服务)
  • 参数:-XX:+UseConcMarkSweepGC

2.G1收集器 (Garbage-First)

  • 算法: 分区 (Region)管理,结合标记-整理和复制算法,预测停顿时间.
  • 场景:大内存(>4GB)、高吞吐与低延迟兼顾 (如大数据应用)
  • 参数:-XX:+UseG1GC

3. ZGC与Shenandoah

  • 算法:基于染色指针和读屏障,实现亚毫秒级停顿 (需JDK11+)
  • 场景:超低延迟要求的实时系统 (如金融交易) )

三、配置与使用指南

1.选择垃圾收集器

  • 串行收集器 (单线程)(适合客户端应用)-XX:+UseSerialGC
  • 并行收集器 (多线程)XX:+UseParallelGC(默认,适合吞吐优先场景)
  • G1收集器XX:+UseG1GC(平衡吞吐与延迟) 

2.调整堆与分代比例

  • 设置堆大小:-Xms(初始堆)-Xmx(最大)
  • 新生代与老年代比例:(老年代是新生代的2倍)-XX:NewRatio=2

3.优化GC日志与监控

  • 启用日志:-Xlog:gc*(JDK9+) 或-XX:+PrintGCDetails
  • 工具分析:JVisualVM、GCViewer解析日志,优化停顿时间与吞吐量

总结

  • Java GC算法的选择需结合应用场景
  • 高吞吐: Parallel Scavenge/Old.
  • 低延迟:CMS、G1或ZGC。
  • 大内存:优先G1或ZGC

jvm基础

一、基本定义与作用

1.虚拟计算机架构
        JVM是一个虚构的计算机系统。通过软件模拟硬件功能,屏蔽底层操作系统差异,实现Java程序*“一次编译,到处运行”**的特性
2.核心功能
        字节码执行:将.class 文件中的字节码转换为机器码执行
        内存管理: 自动分配回收内存 (堆、栈等) ,避免手动管理导致的内存泄漏                                      跨平台支持: 不同操作系统只需安装对应JVM版本,即可运行同一Java程序 

二、核心架构组成

1.类加载子系统 (Class Loader)
        负责加载 class 文件,完成类的加载、链接(验证、准备、解析)和初始化,形成可被JVM直接使用的Java类型
2.运行时数据区

  • 堆(Heap) : 存储所有对象实例数组线程共享,是垃圾回收的主要区域
  • 方法区 (Method Area) : 存储类结构信息 (如类名、方法代码) 、静态变量和常量池
  • 虚拟机栈 (JVM Stack): 线程私有,存储局部变量、方法调用栈帧,方法执行时入栈,结束则出栈   
  • 程序计数器 (PC Register) : 记录当前线程执行的字节码指令地址,确保多线程切换后能恢复执行 
  • 本地方法栈 (Native Method Stack) : 服务于JVM调用的Native方法(如C/C++库

3.执行引擎
        解释器:逐行解释字节码为机器码,启动速度快但执行效率低,
        即时编译器 (JIT) :热点代码编译本地机器码,提升执行效率
        垃圾回收器 (GC): 自动回收堆内存中不再使用的对象,通过分代算法(新生代、老年代)优化回收效率 

三、运行机制

1.类加载流程

  • 按需加载类文件,过程包括加载>验证>准备->解析 -初始化。例如,首次使用 new关键字时触发类加载 

2.线程执行模型

  • 每个线程独立拥有程序计数器、虚拟机栈和本地方法栈,共享堆和方法区,通过线程调度实现并发 

3.跨平台实现

  • Java源码编译为与平台无关的字节码 (.class文件),由JVM根据当前操作系统动态转换为本地机器码执行  

四、核心特点

1.自动内存管理
        通过垃圾回收机制自动释放无用对象内存,开发者无需手动干预,但需避免内存泄漏(如长生命周期对象持有短生命周期引用).
2.安全机制
        提供字节码校验、类加载验证和安全管理器防止恶意代码破坏系统
3.高性能优化
        JIT编译器与自适应优化技术 (如热点代码检测)结合,平衡启动速度运行效率

五、常见JVM 实现

        HotSpot: Oracle官方默认虚拟机,广泛用于生产环境,支持高效的JIT编译和垃圾回收

        GraaIVM: 支持多语言 (Java、Python等)的高性能虚拟机,适用于云原生场景

***************持续更新 易经的方式看技术*************************

***************持续更新 易经的方式看技术*************************

***************持续更新 易经的方式看技术*************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值