JVM面试总结

Java内存模型(JMM)规定了变量如何在主内存和工作内存间交互,确保跨线程的内存可见性。JVM内存包括堆、虚拟机栈、本地方法栈、程序计数器和方法区。垃圾回收机制通过可达性分析判断对象是否可达,使用标记-清除、复制、标记-整理等算法回收内存。CMS和G1是两种常见的垃圾收集器。类加载涉及加载、链接(验证、准备、解析)和初始化四个阶段,双亲委派机制保证类加载有序且安全。
摘要由CSDN通过智能技术生成

1.java内存模型

JMM是java的内存模型,JMM-也叫Java Memory Model,这里反应翻译成存储更好,因为工作内存指的不是内存.而是CPU寄存器,主内存才是内存.屏蔽了各种硬件和操作系统的内存访问差异-把硬件的细节封装起来,实现让java程序在各平台下都能达到一致的内存访问效果,它定义了jvm如何将程序中的变量在主存中读取

具体定义为:1)所有变量都存在主存中,主存是线程共享区域;

2)每个线程都有自己独有的工作内存,线程想要操作变量就必须从主存中copy变量到自 己的工作区,每个线程的工作内存是相互隔离的

3)由于主存和工作内存之间有读写延迟,且读写不是原子性操作,所以会有线程安全问题

相关问题--内存可见性

2.jvm的内存结构

1)堆.只有堆是线程共享的,JVM进行垃圾回收的主要区域,存放对象的信息,分为新生代和老年代,

线程私有区:

1)虚拟机栈:放了局部变量和方法调用信息 每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧都有方法的参数,局部变量,方法出口等信息,方法执行完毕后释放栈帧

2) 本地方法栈:为native修饰的本地方法提供空间,一般是c语言的方法

3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码,因为指令是字节码文件

程序想要运行,,JVM需要将字节码文件加载到内存上,程序就会一条条把指令从内存读取起来,放到cpu执行,也就要随时记住,当前执行的是哪一条,因为cpu是并发执行程序的,cpu是同时运行所有进程,而操作系统是以线程为单位基本调度执行,每个线程都得记录自己的执行为位置,所以每个线程都有自己的程序计数器

4)方法区:放的是"类对象"

.java-> ,class文件后会被加载到内存中,也就被jvm构建成了类对象

这里的类对象就是放到方法区中,类对象描述了这个类长什么样

类的名字,方法,成员,成员的名字和;类型,是什么权限,每个方法什么名字什么类型,方法里的指令

还有一个静态成员

staitc修饰的成员变量是类属性

普通的成员变量是"实例属性"

3,什么情况下会内存溢出

堆内存溢出:

1)对象一直创建而不回收

2)加载的类越来越多的时候

3)虚拟机栈的线程越来越多

栈溢出

方法调用次数过多,一般是递归不当造成

4.JVM垃圾回收机制

第一步先找垃圾/判断垃圾

第二步释放垃圾

第一步找垃圾:

1)基于引用计数(Phtyon采取的方法

针对每个对象,都会额外引入一小块内存,保存这个对象有多少个引用指向

这个内存不再使用,就释放了

引用计数为0,就不再使用了

通过引用计数来决定对象的生死.

引用计数简单可靠高效但是有缺点

  • 空间利用率低,每个new的对象都得搭配个计数器,如果对象很小,计数器占用的内存就很大,空间就浪费了

  • 循环引用的问题

第一步

第二步

第三步

第四步

此时此刻,两个对象的引用计数不为0.但是由于引用长在彼此身上,所以外界代码就无法访问到

这两个对象就被孤立了.及不能使用,也不能释放.这就是内存泄露

2)基于可达性分析(java采取的方案)

通过额外的线程,定期堆整个内存空间的对象进行扫描

有些起始位置(GCRoots)会类似于深度优先遍历一样,把可以访问到的对象都标记一遍

GCRoots分为三类

  • 栈上的局部变量

  • 常量池里的引用指向的对象

  • 方法区中的静态成员指向的对象

带有标记的就是可达对象,没有标记的就是不可达 就是垃圾

可达性分析的优点就是克服了引用计数的缺点:1.空间利用率低,2.循环引用问题

但是缺点:系统开销大.遍历一次比较慢

第二步,回收垃圾

三种策略:标记-清除 复制算法 3. 标记-整理

1.标记-清除

标记就是可达性分析的过程.清除就是直接释放内存

如果直接释放,被释放的内存是离散的,不是连续的,是分散开的,带来的就是内存碎片问题

2.复制算法

把内存分为两半,只使用一半.进行垃圾回收的时候,现将存活的对象复制到另一块区域,然后清空之前的区域,用在新生代

3.标记整理算法

与标记清除类似,但是在标记之后,把存活的对象向一端移动,然后清除边界外的垃圾对象,用在老年代

4.结合一起---分代回收

针对对象进行分类(根据对象的年龄进行分类)

什么是年龄 就是一个对象熬过一轮GC的扫描就长了一岁

1.刚创建出来的对象,就放在伊甸区

2.如果伊甸区的对象熬过一轮gc扫描就会被拷贝到幸存区(复制算法

3.在后续的几轮CG中,幸存区额度对象就会在两个幸存区之间来回拷贝(复制算法

每一轮都会淘汰一波

4.在持续若干轮后,进入老年代(标记-整理

5.特殊情况

大对象,占用内存多的对象直接进入老年代,因为大对象拷贝开销大,不适合使用复制算法

5.典型的垃圾回收器

1).CMS收集器

标志就是STW时间短

他基于可达性分析:

1)初始标记找到GCRoots.速度很快,会引起短暂的stw

2)并发标记,虽然速度慢,但是可以和业务线程并发执行,不会引起STW

3)重新标记,因为2)中的业务线程可能会影响并发标记的结果,针对2)的结果进行微调.会引起stw但是快

4)回收内存,也是和业务进程并发

2).G1收集器

唯一的全区域的垃圾回收期

把整个内存分成了很小的区域--Region

给这些Region进行了不同的标记

然后扫描的时候一次那个扫描若干个Region,不追求一轮GC就扫描,

6.类加载的过程

是运行环境的时候一个重要核心功能,

目标:把.class文件加载到内存中,构建成类对象

1)Loading环节

找到对应的.class文件,打开并读取.class文件,同时初步生成一个大概的类对象

Loading最关键的一个环节,就是读取解析class文件

把读取解析得到的信息初步填写到类对象中

2)Linking环节

建立多个实体之间的联系

① Verification 检验

检验读到的内容格式是否和规范规定的格式是否完全匹配.如果发现读到的数据格式不符合规范.就会类加载失败,并抛出一个异常

② Preparation 准备

正式为类中定义的变量,就是静态变量,分配内存并设置变量初始量的阶段

第一步给静态变量分配内存

第二步,给它设置到0值

第三步,给它在内存上设置编号,为了最后一个阶段做准备

③Resolution 解析

java虚拟机将常量池的符号引用替换为直接饮用的过程,也就是初始化常量的过程

因为.class文件的常量是集中防止的,每个常量都有一个编号

resolution阶段需要根据编号找到对应内容并填充到类对象中

3)initializing 初始化

真正的对类对象进行初始化,尤其针对静态成员

7.双亲委派机制

JVM提供了专门的对象,叫类加载器,负责进行类加载

找文件的过程也是通过类加载器来负责的

.class文件可能放置的位置有很多,有的要放到jdk目录里,有的要放到项目目录里

还有在其他特定位置

因此,JVM里面提供了多个类加载器,每个类加载器负责了一个片区

默认的类加载器 主要有三个

1)BootStrapClassLoader -负载加载标准库中的类

2)ExtensionClassLoader -负责加载JDK拓展的类

3)ApplicationCLassLoader-负责加载当前目录中的类

当一个类加载器收到类加载请求时,会先把这个请求交给父类加载器处理,若父类加载器找不到该类,再由自己去寻找。

该机制可以避免类被重复加载,还可以避免系统级别的类被篡改

8.jdk,jre和jvm

  • jdk:java开发工具包.包括jre(java运行环境),java工具,java基础类库

  • jre:java运行环境,包括jvm标准实现以及java核心类库

  • jvm:java虚拟机,一种抽象化的计算机

9.对象头中的信息

对象头有两个部分,一部分是MarkWork,存储对象运行时的数据,比如对象的hashcode,GC分代年龄,GC标记,锁的状态,获取到锁的线程ID等;

另外一部分是表明对象所属类,如果是数组,还有一个部分存放数组的长度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值