JVM

1. JVM内存模型:堆、栈、程序计数器、方法区

  1. 堆:线程公有。是JVM中最大的一部分,用于存放new创建的对象,GC回收主要回收的就是堆的内存,堆又可以分为老年代和年轻代
    1. 年轻代:用于存放新生对象和短期存活的对象,年轻代又可以分为eden区、FS区、TS区。其中大部分对象会直接进入eden区,当eden区满了之后,会执行Minorgc,Minorgc会发生STW,停止用户线程,回收年轻代的内存空间。如果经过多次gc后依旧存活的对象,JVM默认设定为15次,会移动到老年代
    2. 老年代用于存放大对象和长期存活的对象,大对象会直接进入老年代,当老年代满了之后,会执行Fullgc,回收堆和方法区的内存
    3. MinorGC:回收年轻代的空间
    4. MajorGC:回收老年代的空间
    5. Fullgc:回收堆 + 方法区的内存,堆进行垃圾回收,方法区进行类卸载。
      1. system.gc()执行时
      2. 老年代空间不足时
      3. 方法区空间不足时
      4. 空间担保机制
    6. 为什么分为老年代和年轻代:
      1. 针对不同对象的存活时间不同分别进行管理,比如年轻代大部分对象其实是短期存活的对象,频繁执行Minorgc,对象使用完之后能够及时被回收,释放内存。而老年代的对象是长期存活的或者是大对象,不需要经常进行gc回收。如果将全部对象放在一起,每次gc都需要遍历整个堆空间,回收效率低
    7. 堆会发生OOM
  2. 栈:线程私有。分为Java虚拟机栈和本地方法栈
    1. Java虚拟机栈:对应Java方法,每当有新线程创建时就会分配一个栈空间,线程结束后栈空间被回收,创建一个栈帧,存放局部变量表,局部变量表上存放8种基本数据类型和对象的引用指针。Java虚拟机栈会发生SOF和OOM
    2. 本地方法栈:对应system本地方法,同样会发生SOF和OOM
  3. 程序计数器:线程私有。每个线程都有一个程序计数器,是当前线程在字节码文件的行号指示器,单线程下可有可无,多线程下发生线程切换时,程序计数器会记录当前线程在字节码文件执行的行号位置,以便线程再次获得调度时能快速恢复状态并继续执行
  4. 方法区:线程公有。存放静态变量、静态方法、常量。运行时常量池也存放在方法区。1.8之前使用永久代实现,1.8之后使用元空间实现,元空间不属于JVM内存
    1. 方法区也会参与名为类卸载的gc回收,当堆中没有该类的实例、加载该类的类加载器已经被回收、类的class对象被回收后,可以进行类卸载
    2. 方法区也会发生OOM

2. 哪些区域可能发生OOM:堆、栈、方法区

    1. 堆内存不足,通过jmap查看堆内存使用情况,使用-Xmx和-Xms调整堆内存大小
    2. 内存泄漏,存在未被回收的对象,使用可达性分析查找回收
    1. 运行时大量创建线程,Java虚拟机栈内存不足以为线程分配栈空间导致OOM,可以减少使用线程或者-Xss调整线程大小
    2. 此外Java虚拟机栈还会发生SOF栈溢出,当递归深度超过JVM的规定时,发生SOF
  1. 方法区
    1. 运行时大量创建类,比如反射,类信息会存放在方法区,导致方法区内存不足
    2. 1.8之前使用永久代实现,通过-XX:MaxPermSize调整
    3. 1.8之后使用元空间实现,通过-XX:MetaSpaceSize调整

3. 类加载的时机:5个

  1. new实例化
  2. 反射获取class对象
  3. main方法的类最先加载
  4. 访问类的静态变量和静态方法时,类未实例化
  5. 初始化子类时,父类未被初始化

4. 类加载过程:加载、连接、初始化

  1. 加载:将字节码文件加载到JVM中,将静态变量、常量、静态方法加载到方法区,生产一个class对象
  2. 连接
    1. 验证:检查字节码文件是否符合JVM规范,防止出现错误
    2. 准备:为静态变量分配内存空间并赋初值,主要是分配空间
    3. 解析:将常量的符号引用转换为直接引用
  3. 初始化:真正为静态变量分配初值

5. 类加载器

  1. BootstrapClassLoader,启动类加载器,加载lib目录下rt.jar
  2. ExtensionClassLoader,拓展类加载器,加载lib目录ext文件夹
  3. ApplicationClassLoader,应用类加载器,加载用户类路径下的类
  4. 自定义的类加载

6. 双亲委派

  1. 过程:当一个类加载器接到加载任务时,先检查这个类是否已经被加载,如果是,返回class对象,否则将加载任务交给父加载器进行加载,当父加载器无法加载时,才会由子加载器进行加载
  2. 好处:
    1. 沙盒安全机制:防止用户自定义的类覆盖核心基础类
    2. 避免类的重复加载
  3. 破坏:重写loadclass方法。保护:添加findclass方法

7. GC回收

1. 标记阶段

  1. 引用计数法:每个对象都有一个引用计数器,有引用计数+1,释放引用计数-1,0表示可以回收。
    1. 优点:实时计算,效率高
    2. 缺点:实时计算,时间开销大,需要一个计数器,有额外的空间开销。无法解决循环引用问题
  2. 可达性分析:从GCRoots开始,当一个对象进入到GCRoots中没有任何一条引用链与之相连时,表明该对象可以被回收
    1. 优点:解决了循环引用问题
    2. 缺点:效率比引用计数法低
  3. GCRoots:Java虚拟机栈和本地方法栈的对象、方法区的静态变量和常量

2. finalized()方法

  1. 对象会经历两次标记过程才能被认定为可回收,第一次由GCRoots进行标记,第二次在finalized()方法中进行,如果执行finalized()之后对象还是没有任何一条引用链与GCRoots相连,就是可以被回收

3. 对象的引用类型

  1. 强引用:通过new创建的对象,不会被回收
  2. 软引用:非必须的对象,内存不足时被回收
  3. 弱引用:非必须的对象,下一次GC时被回收
  4. 虚引用:可有可无的对象,随时都会被回收

4. 清除阶段

  1. 标记清除法:先对需要存活的对象进行标记,标记完成后清除没有被标记的对象
    1. 缺点:如果堆中存在大量的对象,需要遍历整个堆,标记和清除的效率低,清除过程会产生空间碎片
  2. 复制法:将内存分成大小相等的两块,先使用其中一块,当这一块的内存满了之后,对需要存活的对象进行标记,清除没有标记的对象,然后将存活的对象移动到另一块内存
    1. 优点:不会产生空间碎片,复制法用于年轻代,需要标记的对象少,执行效率高
    2. 缺点:内存的利用率低
  3. 标记整理法:先对需要存活的对象进行标记,标记完成后将他们移动到一边,回收边界外的内存
    1. 优点:不会产生空间碎片
    2. 缺点:用于老年代,需要标记的对象多并且存在大对象,效率低
  4. 分代收集法:hotspot虚拟机针对对象的不同存活时间采用不同的回收策略
    1. 年轻代的对象生命周期短,回收频繁,使用复制算法
    2. 老年代的对象生命周期长并且存在大对象,回收不频繁,采用标记清除和标记整理

4. GC收集器

  1. CMS并发标记清除
    1. 四个阶段:初始标记、并发标记、重新标记、并发清除
    2. 其中初始标记和重新标记会STW
    3. 优点:并发标记清除,效率高
    4. 缺点:基于标记清除算***产生空间碎片
  2. G1并行并发不分代,基于标记整理法,将堆内存分成大小相等的区域,进行筛选回收
    1. 四个阶段:初始标记、并发标记、最终标记、筛选清除
    2. 其中初始标记和最终标记会STW
    3. 优点:并行并发效率高,基于标记整理法,不会产生空间碎片问题

5. 为什么会STW

  1. 确保引用的地址能正确更新,因此GC时可能会设计对象的移动,这时需要先将用户线程停止,等待GC完成之后再重新指向
  2. 防止一直处于标记阶段,如果一边标记一边产生垃圾,GC线程无法完成标记工作,不能进行清除

6. System.gc()一定会执行吗

  1. System.gc()会显示触发FullGC,但是不能保证一定会执行
  2. 一般情况下不需要手动调用System.gc()进行GC回收
  3. 可以调用System.runFinalization()方法强制执行对象的第二次标记,然后再执行一次System.gc()就可以

7. 内存分配原则和空间担保机制

  1. 内存分配原则:大部分对象会进入年轻代的eden区、大对象直接进入老年代、长期存活的对象进入老年代
  2. 空间担保机制:在年轻代进行MinorGC之前,先检查老年代的内存是否足够存放年轻代所有的对象,如果不够,先进行FullGC

8. JVM参数

  1. -Xms:堆初始大小
  2. -Xmx:堆最大大小
  3. -XX:NewRatio:年轻代和老年代的比例,默认是1:2
  4. -XX:SurvivorRatio:eden区、FS区、TS区的比值,默认是8:1:1
  5. -Xss:调整线程大小
  6. -XX:MetaSpaceSize
  7. -XX:PermSize
  8. -XX:MaxTeruningThreshold

8. jvm故障处理工具:jps、jmap、jstack

  1. jps:查看虚拟机进程,显示当前正在运行的虚拟机进程和进程ID
  2. jmap:查看堆内存使用情况和堆所使用的gc收集器。可用于解决堆的oom问题
  3. jstack:查看进程的线程快照,可以定位死锁问题
全部评论

相关推荐

昨天 20:46
武汉大学 Java
点赞 评论 收藏
分享
12-19 16:52
门头沟学院
点赞 评论 收藏
分享
10-31 20:07
门头沟学院 Java
点赞 评论 收藏
分享
rbjjj:太杂了吧,同学,项目似乎都没深度,都是api调度耶,分层架构思想没有体现出来了,前端没有前端优化前端工程化体现,后端微服务以及分层架构没体现以及数据安全也没体现,核心再改改,注重于计算机网络,工程化,底层原理吧
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务