JVM+JUC //想成为Java高手?不精通这俩可不行!

【JVM】

【问】JVM的总体结构?

JVM四大组成:类装载器+运行时数据区+执行引擎+本地接口。

【问】运行时数据区(java内存区域)的内存图?五个部分的详细介绍?

(1)

java8以前:

java8以后:

(2)

a、堆

存放:对象实例。

更新速度慢,二级缓存。程序员分配释放或GC机制。

java7及以前:

java8:

永久代(方法区)被从JVM中移除,用元空间替代,位于直接内存中。

b、方法区(java8移动到元空间)

java7方法区存放:常量/常量池+静态变量+类信息+即时编译后代码。

java8:抛弃方法区,常量+静态变量 存入jvm堆,其余元数据移动到元空间存放。

方法区和永久代的关系?

永久代是HotSpot虚拟机对方法区的⼀种实现方式,把方法区用来当作永久代。

常用参数?

java7:调节方法区大小:

java8:调节元空间大小:

为什么要把永久代替换为元空间?

元空间在直接内存中,不受jvm内存大小限制,溢出概率更小。

c、程序计数器(PC寄存器)

作用:

(1)字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

(2)在多线程的情况下,程序计数器⽤于记录当前线程执行的位置,从⽽当线程被切换回来的时

候能够知道该线程上次运⾏到哪儿了。

注意:

唯一一个不会出现OOM问题的内存区域。

d、JVM栈

存放:对象引用和基本数据类型。

更新速度快,一级缓存。变量生命周期结束自动释放。

线程私有也是线程安全,一个线程对应一个虚拟机栈区域,一个方法的调用对应一个栈帧。(栈的内部由一个个的栈帧组成)(return或报错则弹栈)

e、本地方法栈(native)

调用本地方法(native方法)的区域,连接其他语言。(C/C++)

f、直接内存(不属于JVM,但也被频繁使用)

【问】JVM堆、栈的区别?

栈内存 堆内存
存储对象 对象引用和基本数据类型 对象实例
更新速度 快,一级缓存 慢,二级缓存
内存释放 变量生命周期结束自动释放

程序员分配释放、GC机制

线程 线程私有 线程公有

【问】java程序new一个对象后发生了什么,比如:A a=new A(); ?

编译A类>>类加载流程>>对象创建流程>>对象访问定位

【问】类的加载过程?类的生命周期?

(1)加载(双亲委派机制)

JVM读取Class文件,创建Class对象的过程。

(2)连接

a、验证

确保Class文件符合当前虚拟机的要求。

b、准备

在方法区中为类变量分配内存空间并设置默认值。(加了final修饰的直接准备)

c、解析

将常量池中的符号引用替换为直接引用。

(3)初始化

为类的静态变量赋初始值和收集静态代码块。

【问】类加载机制(双亲委派机制)?作用?

(1)

三个等级的加载器:

(1)根加载器

(2)扩展类加载器

(3)应用程序类加载器(负责加载我们自己写的类)

第一步:

由下往上走的逻辑,加载过的话不用加载;如果都没有加载过,就一直走到最上边(也就是根加载器);

第二步:

假如都没有加载过,走到了根,就采取第二步加载逻辑,由上往下走的逻辑。每到一个加载器判定自己能否加载,自己能加载就自己加载,加载不了就给子加载器加载。一直到最下面的应用程序类加载器(有时还有自定义类加载器,需要继承java.lang.ClassLoader,重写findclass方法)。若还是加载不了,抛出ClassNotFound异常。

(2)

a、第一步的逻辑是为了节省时间,提升性能。

b、第二步的逻辑是为了当修改下层加载器,不影响上层加载器,保证安全性。

【问】反射:

定义:

JAVA反射机制是在运行状态中,通过class类对象动态的获取类的信息或动态调用对象的方法,这种机制称为java的反射机制。

特点:

反射可以实现动态创建对象和编译,体现出很大的灵活性,是Java被视为动态语言的关键。但是会影响一些性能。

【问】Java对象的创建过程?(5步)(运行时数据区里进行)

(1)类加载检查:

虚拟机遇到⼀条 new 指令时,检查是否完成了完整的类加载过程,如果没有,必须执行类加载过程。(加载、连接、初始化)

(2)内存分配:

为新生对象分配堆内存。

内存分配方式?

分配方式有 “指针碰撞” “空闲列表 两种方式。选择哪种分配方式取决于java堆是否有内存碎片(取决于垃圾收集器)

解决内存分配并发问题?

(1)乐观锁CAS+失败重试: CAS 是乐观锁的⼀种实现方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。虚拟机采⽤ CAS配上失败重试的方式保证更新操作的原子性。

(2)独立内存TLAB: 为每⼀个线程预先在 Eden 区分配⼀块独立内存TLAB,JVM 在给线程中的对象分配内存时,首先在TLAB 分配,当对象大于TLAB 中的剩余内存或 TLAB 的内存已⽤尽时,再采⽤上述的CAS进行内存分配.

(3)对象的初始化零值:

将分配到的内存空间都初始化为零值(不包括对象头),保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使⽤,程序能访问到这些字段的数据类型所对应的零值。

(4)设置对象头:

设置对象头信息:这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC 分代年龄等。

(5)执行init构造方法:

接着执行 <init> 方法,把对象按照程序员的意愿进行初始化,这样⼀个真正可用的对象才算完全产生出来。(字段不在是默认值,而是程序员设定的值)

【问】对象的访问定位有哪两种方式?

我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式有虚拟机实现而定,目前主流的访问方式有①使用句柄和②直接指针两种:

1. 句柄池:

Java堆中将会划分出⼀块内存来作为句柄池,reference 中存储的就是对象的句柄地址,⽽句柄中包含了对象实例数据与类型数据各⾃的具体地址信息;

2. 直接指针:

如果使⽤直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference 中存储的直接就是对象的地址。

这两种对象访问方式各有优势:

(1)句柄池的好处:

对象移动时地址修改方便。引用中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,⽽不需要更改引用。

(2)直接指针的好处:

就是访问对象速度快。它节省了⼀次指针定位的时间开销。

【问】堆内存中分配对象的流向?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值