JVM常见面试题

1.JVM有哪些内存区域?

在这里插入图片描述
虚拟机栈:在jvm运行过程中存储当前线程运行方法所需的数据、指令、返回地址;栈的生命周期跟随线程的生命周期;每一个栈包含多个方法,每个方法占用一块空间,称为栈帧;它的结构是栈,是先进后出的;
在这里插入图片描述
本地方法栈:本地方法栈和虚拟机栈的作用、实现类似,它服务的对象是native方法;

程序计数器:主要用来记录各个线程执行的字节码的地址;

方法区:jdk1.7之前叫永久代,是在堆里面,jdk1.8之后叫元空间,另起一块空间,不放在堆里面了;主要存放静态信息,比如类的信息、常量池、方法数据、方法代码,是被所有线程共享的;

:jvm上最大的内存区域,我们申请的几乎所有的对象,都是在堆里面存储。

虚拟机栈、本地方法栈、程序计数器是线程私有的,每个线程都有单独的一份。方法区、堆是线程共享的

2.堆空间大小怎么配置?各区域怎么划分?

Jdk1.8及以后堆空间被划分为新生代、老年代;没有持久代,持久代独立出一块空间,叫方法区(也可以叫永久代、元空间等,不同JDK有不同叫法);
大厂有一套相对成熟的空间划分算法:
堆空间的总大小是: 3~4倍活跃数据大小
新生代:1~1.5倍活跃数据大小
老年代:2~3倍活跃数据大小
方法区的大小是:1.2~1.5倍Full GC后永久代空间占用

例子:活跃数据300M
总堆:300M*4 = 1.2g
新生代:450M
老年代:750M
方法区:300M(假设Full GC后方法区占用200M)

3.jvm中有哪些内存区域会发生内存溢出(OOM)?

Jvm中会发生内存溢出的区域:栈、堆、方法区、本机直接内存;程序计数器是JVM运行过程中唯一不会发生内存溢出的区域。
栈溢出:栈溢出一般是指虚拟机栈,虚拟机栈的大小默认是1M,当方法中出现递归调用时容易发生栈溢出,报StackOverflowError;

堆溢出:当新创建对象大小大于当前堆空间大小,会进行gc,当gc后内存空间仍然不够,就会发生堆溢出,报OutOfMemoryError:Java heap space;

方法区溢出:方法区存放的是类信息、常量等信息,通常很少进行垃圾回收,当这些信息超过方法区大小,会进行方法区垃圾回收,回收后大小仍然不够用,就会发生方法区溢出,报OutOfMemory:Metaspace;

本机直接内存(堆外内存)溢出:本机直接内存也叫堆外内存,就是机器分配给虚拟机后剩余的内存,比如你的机器是16G,JVM占8G,那本机直接内存就剩余8G。在jvm中可以设置最大直接内存的大小,当你分配的直接内存超过最大直接内存,就会发生直接内存溢出,报OutOfMemoryError:Direct buffer memory;

4.jvm在创建对象时采用了哪些并发安全机制?

首先先了解对象创建的过程:
在这里插入图片描述
创建对象发生并发问题主要发生在分配内存的时候,有两种方式解决并发安全:1.CAS加失败重试;2.本地线程分配缓冲

  1. CAS加失败重试:
    CAS是一种乐观锁的形式,CAS机制中使用3个基本操作数:内存地址,旧的预期值,待替换的新值;只有当预期值A和内存地址中的实际值相等时,才能分配成功,失败则进行重试;因为CAS加失败重试采用的是乐观锁的形式,有一定的开销,JDK1.8默认情况下没有使用这种形式。
    在这里插入图片描述

  2. 本地线程分配缓冲(Thread Local Allocation Buffer,TLAB)
    每个线程在java堆中预先分配一小块私有内存,也就是本地线程分配缓冲,这样每个线程都单独拥有一个Buffer,如果需要分配内存,就在自己的Buffer上分配,这样就不存在竞争的情况,可以大大提升分配效率。属于用空间换取时间的策略,JDK1.8中默认使用本地线程分配缓冲来保证多线程创建对象的并发安全。

5.什么是对象头?对象头里面有哪些东西?

在这里插入图片描述
在JVM当中,对象整体的长度(对象头、实例数据、对象填充)必须是8字节,如果对象头+实例数据的实际长度不足8字节,就有对象填充,凑满8字节

6.为什么不要使用finalize()方法?

首先要知道finalize()方法的作用:一个对象要被回收,需要经过两次标记过程,一次是没有找到与GCRoots的引用链,它将被第一次标记。随后进行一次筛选,如果对象没覆盖finalize()方法,则被第二次标记,进行垃圾回收;如果对象覆盖了finalize()方法,我们可以在finalize()方法中使对象变为存活对象。
为什么不要使用finalize()方法:
1.finalize()方法执行线程优先级很低;重写了finalize()方法也不一定被执行;
2.finalize()方法只能执行一次

7.怎么判断对象的存活?

  1. 引用计数法:
    在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零就是非存活对象。
    引用计数法存在一个问题:当对象与对象之间互相引用,即循环引用,两个对象就都是一,根据引用计数法判断,那这两个对象都不是垃圾,而实际这两个对象都是垃圾,所以需要使用额外的机制来解决循环引用。所以JVM通常不使用引用计数法来判断对象是否存活。

  2. 可达性分析算法(常用):
    将对象及其引用关系看做一个图,通过一系列称为GC Root的根对象作为起始节点集,从这些节点开始,通过引用关系向下搜索,搜索过程所走过的路径称为引用链,如果一个对象和GC Root之间不可达,也就是不存在引用链,那么即认为是可回收对象。也解决了循环引用的问题。
    GC Root:局部变量、静态变量、常亮、JNI指针
    在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chenzm666666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值