鸡汤一碗
要想青春不留遗憾,小伙必须敢想敢干
大纲
- 概念以及运行机制
- 运行时数据区
- 垃圾回收
- java类加载机制
知识点
1. 概念以及运行机制
JVM :Java Virtual Machine ,它是用来运行java字节码的虚拟机。它由五部分组成:类加载子系统、运行时数据区、执行引擎、本地接口库、本地方法库;
GC : 垃圾回收器
java源代码通过编译器编译成.class文件(字节码), 然后类加载子系统把 .class 加载到JVM虚拟机, JVM虚拟机(JIT)把字节码翻译成机器码, 机器码调用相应的本地方法库执行;
2.运行时数据区
运行时数据区分为 虚拟机堆(新生代1/3、老年代2/3)、虚拟机栈、方法区、程序计数器、本地方法区;
线程私有区域
- 程序计数器 : 线程执行字节码的行号指示器
- 虚拟机栈 :描述java方法执行过程的内存模型; 当前线程帧中存储了局部变量表、操作数、动态链接、方法出口信息等;
jvm参数 Xss(stack space)
- 本地方法区 :与虚拟机栈相似、它执行的是Native方法服务;
线程共享区域
- 方法区 也叫永久代。存储常量、静态变量、类信息、JIT编译后的机器码
- 虚拟机堆 : JVM运行过程中创建的对象、产生的数据都存储在堆中。它分为新生代、老年代和永久代;
jvm参数 Xms(最小堆内容) Xmx(最大堆内存)
新生代默认情况下占1/3堆空间, 老年代默认占2/3堆空间,永久代占非常少的空间。 新生代又分为 Eden(8/10)区、ServivorFrom区和ServivorTo区各1/10
直接内存(NIO)
引申知识点: OOM ,Full GC , JVM调优
OOM - Out Of Memory (内存溢出)
Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collecto
- 为什么会出现?
# StackOverflowError
线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。递归调用,缺递归终止条件。
# java.lang.OutOfMemoryError: Java heap space
Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常
# java.lang.OutOfMemoryError:GC overhead limit exceeded
GC回收时间长时会抛出OutOfMemoryError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存,连续多次GC都只回收了不到2%的极端情况下才会抛出
# java.lang.OutOfMemoryError:Direct buffer memory
- 怎么排查问题?
事前 : 调整jvm 参数, 在OOM的错误发生的时候, 生成错误日志文件;
XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/xxx.hprof -XX:ErrorFile=${LOG_DIR}/hs_err_%p.log -XX:+PrintCommandLineFlags -XX:-OmitStackTraceInFastThrow
事中 :
# 分析生产日志
- 通过登陆linux, grep分析日志中的异常信息。第二步是分析异常日志中提到的代码
# 使用 jvisualvm 进行分析
jvisualvm配置参考 : https://blog.csdn.net/u012550080/article/details/81605189
3.垃圾回收
GC的概念 : garbage collection .
GC 回收的对象是对象到任何GC root没有引用链相连(可达性分析);
Java对象引用:强(把一个对象赋值给引用变量),软(SoftReference,内存不足时被收回),弱(WeakReference,垃圾回收时一定被收回),虚
Full GC :
为啥会发生full gc ?
1. 老年代空间不足
频繁full gc的常见原因 full gc 触发条件是 老年代空间不足, 所以追因的方向就是导致 老年代空间不足的原因: 大量对象频繁进入老年代 + 老年代空间释放不掉 系统并发高、执行耗时过长,或者数据量过大,导致 young gc频繁,且gc后存活对象太多,但是survivor 区存放不下(太小 或 动态年龄判断) 导致对象快速进入老年代 老年代迅速堆满 - 发程序一次性加载过多对象到内存 (大对象),导致频繁有大对象进入老年代 造成full gc - 存在内存溢出的情况,老年代驻留了大量释放不掉的对象, 只要有一点点对象进入老年代 就达到 full gc的水位了 - 元数据区加载了太多类 ,满了 也会发生 full gc - 堆外内存 direct buffer memory 使用不当导致 - 也许, 你看到老年代内存不高 重启也没用 还在频繁发生full gc, 那么可能有人作妖,在代码里搞执行了 System.gc(); 定位思路 - 如果有监控,那么通过图形能比较直观、快速的了解gc情况; - 如果没有监控,那么只能看gc日志或jstat来分析 这是基本技能 一定要熟练 - 观察年轻代 gc的情况,多久执行一次、每次gc后存活对象有多少 survivor区多大 存活对象比较多 超过survivor区大小或触发动态年龄判断 => 调整内存分配比例 - 观察老年代的内存情况 水位情况,多久执行一次、执行耗时多少、回收掉多少内存 如果在持续的上涨,而且full gc后回收效果不好,那么很有可能是内存溢出了 => dump 排查具体是什么玩意 如果年轻代和老年代的内存都比较低,而且频率低 那么又可能是元数据区加载太多东西了 其实如果是自己负责的系统,可能要看是不是发版改了什么配置、代码
参考文献地址: https://www.cnblogs.com/mushishi/p/14553105.html
发生 full gc 怎么办?
1. jps -v (查看全部java进程以及jvm参数)
2. jstat -gcutil {pid} (显示xxx的堆分配情况以及垃圾回收次数时间)
3. jstack {pid} (查看线程堆栈)
4. jmap -dump:live,format=b,file=dump.hprof {pid} (用 IBM Memory Analyzer 观察分析)
4.java类加载机制
双亲委派
简单一句:类加载请求,委派给父类的类加载器完成。仅在父类的类加载器不能加载,子类加载器才会尝试自己加载。
引申知识点:SPI (Service Provider Interface)
API (Application Programming Interface)在大多数情况下,都是实现方制定接口并完成对接口的实现,调用方仅仅依赖接口调用,且无权选择不同实现。 从使用人员上来说,API 直接被应用开发人员使用。 SPI (Service Provider Interface)是调用方来制定接口规范,提供给外部来实现,调用方在调用时则选择自己需要的外部实现。 从使用人员上来说,SPI 被框架扩展人员使用。
后面设专题聊这个知识点
实操
暂无
总结
无