JVM篇
JVM
内存模型
JVM中堆和栈有什么区别?
1.用途:栈存方法;堆存实体对象。
2.生命周期:栈中方法的局部变量会随方法调用结束消失;堆中对象声明周期不确定,在垃圾回收机制确认没用了才回收。
3.存取速度:栈快;堆慢。
4.存储空间:栈小,操作系统管理;堆大,JVM管理。
5.可见性:栈私有,每个线程都有栈;堆共享,任何线程都能访问。
栈中存的是指针?
栈存的是对象的引用,也可以用指针说明,在调用方法时,会指向堆中的对象实例,操作堆的对象实例。
堆分类
如果有个大对象一般是在哪个区域?
大对象通常会直接分配到老年代。
程序计数器的作用,为什么是私有的?
因为CPU的机制,CPU会给每个线程分发时间片。当线程1的时间结束时,即便未完成完所有任务,会切换到线程2,执行到线程2的时间结束,再从线程1未完成的部分开始。
程序计数器用来记录当前线程所执行的字节码的行号指示器。
方法区中的方法的执行过程?
解析方法调用,栈帧创建,执行方法,返回处理。
String保存在哪里?
字符串常量池。它的值不可变,可以被多个引用。
String s = new String(“abc”)执行过程中分别对应哪些内存区域?
如果初次创建,则创建两个对象,一个在字符串常量池,一个在堆。如果字符串常量池已经有,则只会创建堆中一个对象。
引用类型有哪些?有什么区别?
分为强软弱虚:
- 强引用,普遍的赋值关系,如A a = new A(),永远不会被GC(Garbage Collection)
- 软引用,SoftReference。系统在发生内存溢出前会对这类对象回收。
- 弱引用,WeakReference。弱引用的对象下一次GC一定会回收。
- 虚引用,同样的当发生GC的时候,虚引用也会被回收。用来管理堆外对象。
弱引用了解吗?举例说明在哪里可以用?
在java中,弱引用通过WeakReference类实现。弱引用的一个主要用途是创建非强制性对象引用,当内存压力过大时被垃圾回收清理,从而避免内存泄漏。
使用场景:
- 缓存系统:JVM在系统内存压力大时,清理弱引用对象。
- 对象池:在对象池中,弱引用用来管理暂时不用的对象。
- 避免内存泄漏:当一个对象不应该被长期引用时,使用弱引用可以防止该对象被意外地保留,从而避免潜在的内存泄露。
内存泄漏和内存溢出
内存泄漏:程序在运行中,存在不再使用的对象仍然被引用,无法被垃圾回收,导致内存被不断占用。
内存泄漏常见原因:
- 静态集合:使用静态数据结构(如HashMap等存储结构)未及时清理。
- 事件监听:未及时取消对事件的监听,导致该事件持续被引用。
- 线程:未停止的线程可能持有对象引用,无法被回收。
内存溢出:OutOfMemoryError,JVM在申请内存的时候,无法找到足够的内存。
内存溢出常见原因:
- 大量对象创建:程序不断创建对象,超出JVM堆的限制。
- 持久引用:大型数据结构长时间持有引用,占据大量内存。
- 递归调用:深度递归导致栈溢出。
JVM内存结构的几种溢出情况
堆溢出:大对象分配时,没有足够空间。
栈溢出:深度递归导致。
元空间溢出:系统的代码非常多,引用的第三方包非常多,通过动态代码生成类加载等方法
直接内存溢出:在使用ByteBuffer中的allocateDirect()的时候会用到,很多JavaNIO(像netty)的框架中被封装为其他的方法,出现该问题时会抛出Java.lang.OutOfMemoryError: Direct buffer memory异常。
内存泄漏和内存溢出的例子
静态属性导致内存溢出:用static创建修饰的对象太多,static修饰的变量会直到应用结束才回收。
未关闭的资源:数据库链接,输入流和session对象。
使用ThreadLocal:线程ThreadLocal的实现中,每个线程都维护一个ThreadLocalMap映射表。当设置set ThreadLocalMap中就会出现key为null的Entry。如果当前线程不结束,改引用就不会被回收,导致内存泄露。
解决方法:1.使用remove彻底清除;2.不适用set(null),因为为空还是存在。3.在final中写结束线程。
类初始化和类加载
创建对象过程
类加载检查——分配内存——初始化零值——对象头——构造函数Init
类加载器有哪些?
启动类加载器——拓展类加载器——系统类加载器/应用类加载器——自定义类加载器
双亲委派模型
双亲委派模型:当某一加载器收到类加载请求,就会从上一级类加载器中寻找,每级都是如此。
如果没有再返回下一级。
作用:
- 保证类的唯一性。
- 保证安全性。
- 支持隔离和层次划分。
- 简化了加载流程。
类加载流程
七个阶段:加载——验证——准备——解析——初始化——使用——卸载
主要分为:加载——连接——初始化
加载:变为.class.jar这类
垃圾回收
什么是垃圾回收?如何触发?
垃圾回收是自动释放不再被程序引用的对象所占用的内存。
触发:
- 内存不足时。
- 手动请求。
- JVM参数。
- 对象数量或内存达到JVM阈值。
判断垃圾回收的方法
1.引用计数法
2.可达性分析算法:从GC Root出发,当引用对象和GC Root没有连接,就代表该引用对象是没被引用的。就可出发垃圾回收。
垃圾回收算法有哪些?
- 标记清除
- 标记整理
- 复制算法
- 分代回收算法
minorGC、majorGC、fullGC的区别,什么场景触发full GC
MinorGC:新生代触发,将存活对象移入老生代。
MajorGC:主要针对老年代触发,清理老年代。
FullGC:清理所有堆内存,会触发stop the world.
触发条件:1.直接调用命令。2.MinorGC存活对象无法全部放到老年代出发。3.JDK8以前永久代空间不足,JDK8以后,元空间空间不足。
垃圾回收器有哪些?
CMS和G1的区别
区别一:使用范围不一样:
- CMS是在老年代,可以配和新生代收集器使用。
- G1收集范围是老年代和新生代。
区别二:STW的时间:
- CMS以最小停顿时间为目标
- G1可预测垃圾回收停顿时间
区别三:垃圾碎片:
- CMS使用“标记-清理”算法,容易产生内存碎片
- G1使用“标记-整理”算法,不产生内存碎片
使用场景:
CMS使用场景:
- 低延迟需求:
- 老年代收集:
- 碎片化管理:
G1使用场景:
- 大堆内存
- 对内存碎片敏感
- 比较平衡的性能
GC会对哪些内存垃圾回收
1.堆。
2.方法区。