Python 官方文档:入门教程 => 点击学习
目录1、请你简述一下 Java 内存结构(运行时数据区)① 程序计数器② 虚拟机栈③ 本地方法栈④ 堆⑤ 方法区⑥ 运行时常量池2、请问JVM垃圾回收是否涉及栈内存?3、虚拟机栈内存
如图所示:
Java指令执行流程:
如图所示:
实例代码:
public class Demo01 {
public static void main(String[] args) {
methodA();
}
private static void methodA() {
methodB(1, 2);
}
private static int methodB(int a, int b) {
int c = a + b;
return c;
}
}
流程分析:
我们打断点来Debug 一下看一下方法执行的流程:
接这往下走,使方法B执行完毕:
然后方法A 执行完毕,其对应的栈帧出栈,main 方法对应的栈帧为活动栈帧;最后main执行完毕,栈帧出栈,虚拟机栈为空,代码运行结束!
本地方法栈:线程私有。本地方法栈与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。
一些带有native 关键字修饰的方法就是需要JAVA去调用本地的C或者c++方法,因为JAVA有时候没法直接和操作系统底层交互,所以需要用到本地方法!
堆:线程共享。Java堆是Java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。Java堆的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。 通过new关键字创建的对象都会被放在堆内存。方法体中的引用变量和基本类型的变量都在栈上,其他都在堆上。Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC 堆”(Garbage)。-Xmx -Xms:JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64。
方法区:线程共享。方法区用于存储已被虚拟机加载的 *类信息(构造方法、接口定义)、常量、静态变量、即时编译器编译后的代码(字节码)*等数据。 方法区在 JVM 启动的时候被创建,并且它的实际的物理内存空间和 Java堆一样都可以是不连续的, 关闭 Jvm 就会释放这个区域的内存。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出错误:(java.lang.OutOfMemoryError:PermGen space、java.lang.OutOfMemoryError:Metaspace)。 注意:方法区时一种规范,而永久代和元空间是它的2种实现方式。
方法区的演进:
1.6 版本方法区是由 永久代 实现(使用堆内存的一部分作为方法区),且由JVM 管理。由Class、ClassLoader、常量池(包括StringTable) 组成。
jdk 1.7 版本仍有永久代,但已经逐步 " 去永久代 ",StringTable、静态变量从永久代移除,保存在堆中。
1.8 版本后,方法区交给本地内存管理,而脱离了JVM,由元空间实现(元空间不再使用堆的内存,而是使用本地内存,即操作系统的内存),由Class、ClassLoader、常量池(StringTable 被移到了堆中管理) 组成。
常量池:可以看做是一张表,虚拟机指令根据这张常量表找到要执行的 类名,方法名,参数类型、字面量 等信息。
常量池是*.class文件中的,当该类被加载以后,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实内存地址。
运行时常量池:是方法区的一部分。
String str = new String("hello");
上面的语句中变量 str 放在栈上,用 new 创建出来的字符串对象放在堆上,而hello这个字面量是放在堆中。
不需要。因为虚拟机栈中是由一个个栈帧组成的,在方法执行完毕后,对应的栈帧就会被弹出栈。所以无需通过垃圾回收机制去回收内存。
我们来看一张图:
举例:如果物理内存是500M(假设),如果一个线程所能分配的栈内存为2M的话,那么可以有250个线程。而如果一个线程分配栈内存占5M的话,那么最多只能有100 个线程同时执行!
我们通过两张图去分析一下:
情况一:
情况二:
从图中得出:局部变量如果是静态的可以被多个线程共享,那么就存在线程安全问题。如果是非静态的只存在于某个方法作用范围内,被线程私有,那么就是线程安全的!
再来看一个案例:
public class Demo02 {
public static void main(String[] args) {// main 函数主线程
StringBuilder sb = new StringBuilder();
sb.append(4);
sb.append(5);
sb.append(6);
new Thread(() -> {// Thread新创建的线程
m2(sb);
}).start();
}
public static void m1() {
// sb 作为方法m1()内部的局部变量,是线程私有的 ---> 线程安全
StringBuilder sb = new StringBuilder();
sb.append(1);
sb.append(2);
sb.append(3);
System.out.println(sb.toString());
}
public static void m2(StringBuilder sb) {
// sb 作为方法m2()外部的传递来的参数,sb 不在方法m2()的作用范围内
// 不是线程私有的 ---> 非线程安全
sb.append(1);
sb.append(2);
sb.append(3);
System.out.println(sb.toString());
}
public static StringBuilder m3() {
// sb 作为方法m3()内部的局部变量,是线程私有的
StringBuilder sb = new StringBuilder();// sb 为引用类型的变量
sb.append(1);
sb.append(2);
sb.append(3);
return sb;// 然而方法m3()将sb返回,sb逃离了方法m3()的作用范围,且sb是引用类型的变量
// 其他线程也可以拿到该变量的 ---> 非线程安全
// 如果sb是非引用类型,即基本类型(int/char/float...)变量的话,逃离m3()作用范围后,则不会存在线程安全
}
}
所以,该面试题答案是:
如果方法内局部变量没有逃离方法的作用范围,则是线程安全的。如果局部变量引用了对象,并逃离了方法的作用范围,则需要考虑线程安全问题。
如图所示,就是栈中栈帧过多的情况:
为什么要用元空间取代永久代?
因为永久代有以下几个弊端:
① 字符串常量池存在于永久代中,在大量使用字符串的情况下,非常容易出现OOM的异常。
② JVM加载的class的总数,方法的大小等都很难确定,因此对永久代大小的指定难以确定。太小的永久代容易导致永久代内存溢出,太大的永久代则容易导致虚拟机内存紧张,空间浪费。
③ 永久代进行调优很困难:方法区的垃圾收集主要回收两部分,常量池中废弃的常量和不再使用的类。而不再使用的类或类的加载器回收比较复杂,FULL GC 的时间长。
以 JDK 8 为例:
名称 | 加载哪的类 | 说明 |
---|---|---|
Bootstrap ClassLoader(启动类加载器) | JAVA_HOME/jre/lib | 无法直接访问 |
Extension ClassLoader(扩展类加载器) | JAVA_HOME/jre/lib/ext | 上级为 Bootstrap,显示为 null |
Application ClassLoader(应用程序类加载器) | classpath | 上级为 Extension |
自定义类加载器 | 自定义 | 上级为 Application |
类加载器的优先级(由高到低):启动类加载器 -> 扩展类加载器 -> 应用程序类加载器 -> 自定义类加载器。
类加载的过程包括:加载、验证、准备、解析、初始化。其中验证、准备、解析统称为连接。
如图所示:
什么是双亲委派模型?
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
为什么要使用双亲委派模型呢?(好处)
避免重复加载 + 避免核心类篡改
① 物理地址方面的区别:
② 内存分配方面的区别:
③ 存放的内容方面的区别:
注:静态变量放在方法区,而静态的对象还是放在堆。
④ 线程共享方面的区别:
文章会不定时更新,有时候一天多更新几篇,如果帮助您复习巩固了知识点,后续会亿点点的更新!也希望大家关注编程网其他文章!
--结束END--
本文标题: 打卡每日10道面试题——JVM篇
本文链接: https://www.lsjlt.com/news/128201.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0