JVM内存结构和Java内存模型都是面试的热点问题,名字看感觉都差不多,网上有些博客也都把这两个概念混着用,实际上他们之间差别还是挺大的。 说到JVM内存结构,就不会只是说内存结构的5个分区,而是会延展到整个JVM相关的问题,所以先了解下JVM的构成。 栈:线程运行需要的内存空间 栈帧:每一个方法运行需要的内存(包括参数,局部变量,返回地址等信息) 每个线程只有一 个活动栈帧(栈顶的栈帧),对应着正在执行的代码 常见问题解析 垃圾回收是否涉及栈内存:不涉及,垃圾回收只涉及堆内存 栈内存分配越大越好吗:内存一定时,栈内存越大,线程数就越少,所以不应该过大 方法内的局部变量是否是线程安全的: 栈内存溢出(StackOverflowError) 栈帧过多 栈帧过大 线程运行诊断 CPU占用过高(定位问题) 程序运行很长时间没有结果(死锁问题) 通过new关键字创建的对象都会使用堆内存 堆是线程共享的 堆中有垃圾回收机制 堆内存溢出(OutOfMemoryError) 堆内存诊断 命令行方式 jconsole jvisualvm StringTable特性 常量池中的字符串仅是字符,第一次使用时才变为对象 利用串池机制,避免重复创建字符串 字符串常量拼接原理是StringBuilder(1.8) 字符串常量拼接原理是编译器优化 StringTable在1.6中存放在永久代,在1.8中存放在堆空间 intern方法主动将串池中没有的字符串对象放入串池 1.8中:尝试放入串池,如果有就不放入,只返回一个引用;如果没有就放入串池,同时返回常量池中对象引用 1.6中:尝试放入串池,如果有就不放入,只返回一个引用;如果没有就复制一个放进去(本身不放入),同时返回常量池中的对象引用 字符串常量池分析(1.8环境) JVM调优三大参数(如: java -Xms128m -Xmx128m -Xss256k -jar xxxx.jar) JVM内存结构中堆和栈的区别 判断对象的引用数量来决定对象是否可以被回收 每个对象实例都有一个引用计数器,被引用则+1,完成引用则-1 优点:执行效率高,程序执行受影响小 缺点:无法检测出循环引用的情况,导致内存泄露 Java虚拟机中的垃圾回收器采用可达性分析来探索所有存活对象 扫描堆中的对象,看是否能沿着GC Root对象为起点的引用链找到该对象,找不到则可以回收 哪些对象可以作为GC Root 通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root 虚拟机栈中的引用的对象 本地方法栈中JNI(natice方法)的引用的对象 方法区中的常量引用的对象 方法区中的类静态属性引用的对象 处于激活状态的线程 正在被用于同步的各种锁对象 GC保留的对象,比如系统类加载器等。 分代垃圾回收流程 默认堆内存分配 GC相关VM参数 安全点(SafePoint) 分析过程中对象引用关系不会发生改变的点 产生安全点的地方: 安全点的数量应该设置适中 串行(SerialGC) 吞吐量优先(ParallelGC) 响应时间优先(CMS -XX:+UseConcMarkSweepGC 标记清除算法) 多线程的垃圾回收器 堆内存较大,多核CPU,Server模式下默认的老年代垃圾回收器 尽可能让单次STW暂停时间最短 部分时期内可以并发执行 执行流程 G1(-XX:+UseG1GC 复制+标记清除算法) 垃圾回收阶段 新生代垃圾收集 新生代垃圾收集+并发标记 混合收集,对新生代,幸存区和老年代都进行收集 Full GC SerialGC ParallelGC CMS 新生代内存不足发生的垃圾收集:minor GC 老年代内存不足 G1 新生代内存不足发生的垃圾收集:minor GC 老年代内存不足,达到阈值时进入并发标记和混合收集阶段 强引用 软引用 仅有【软引用】引用该对象时,在垃圾回收后,内存仍不足时会再次发起垃圾回收,回收软引用对象 可以配合引用队列来释放软引用自身 创建一个软引用:SoftReference ref = new SoftReference<>(new Object()); 软引用被回收后,仍然还保留一个null,如将软引用加入集合,回收后遍历集合仍然还存在一个null 弱引用 虚引用 终结器引用 加载 链接 校验:检查加载的的Class的正确性和安全性 准备:为类变量分配存储空间并设置类变量初始值 解析:JVM将常量池内的符号引用转换为直接引用 初始化 需求场景 步骤 案例演示 创建自定义类加载器 调用自定义类加载器加载类 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。 第三方应用开发过程中,会需要某个类的某个成员变量、方法或是属性是私有的或者只对系统应用开放,就可以通过Java的反射机制来获取所需的私有成员或者方法 代表类的实体,在运行的Java应用程序中表示类和接口 Filed代表类的成员变量(属性) 定义一个Robot类 编写一个反射应用类,针对私有的属性和方法必须设置setAccessible(true)才能进行访问 -通俗来说,JMM是一套多线程读写共享数据时,对数据的可见性,有序性和原子性的规则 JVM实现不同会造成“翻译”的效果不同,不同CPU平台的机器指令有千差万别,无法保证同一份代码并发下的效果一致。所以需要一套统一的规范来约束JVM的翻译过程,保证并发效果一致性 循环创建两类线程,一个线程用于做值的交换,一个线程用于打印值 比较直观的三种结果 实际上除了很容易想到的三种情况外还有一种特殊情况:b = 3 , a = 1 什么是volatile 什么时候适合用vilatile volatile的作用 volatile的性能 什么是happens-before规则:前一个操作的结果可以被后续的操作获取。 如果有用,点个赞再走吧
通俗点说,JVM内存结构是与JVM的内部存储结构相关,而Java内存模型是与多线程编程相关,本文针对这两个总是被混用的概念展开讲解。JVM内存结构
JVM构成
JVM内存结构
程序计数器
(通过移位寄存器实现)
虚拟机栈
本地方法栈
堆
方法区
String s1 = "a"; String s2 = "b"; String s3 = "a"+"b"; String s4 = s1+s2; String s5 = "ab"; String s6 = s4.intern(); System.out.println(s3==s4);// s3在常量池中,s4在堆上(intern尝试s4放入常量池,因为ab存在了就拒绝放入返回ab引用给s6,s4还是堆上的) System.out.println(s3==s5);// s3在常量池中,s4也在常量池中(字符串编译期优化) System.out.println(s3==s6);// s3在常量池中,s6是s4的intern返回常量池中ab的引用,所以也在常量池中 String x2 = new String("c")+new String("d"); String x1 = "cd"; x2.intern(); System.out.println(x1==x2);//x2调用intern尝试放入常量池,但常量池中已经有cd了,所以只是返回一个cd的引用,而x2还是堆上的引用
GC垃圾回收机制
1. 垃圾判别方法
引用计数算法
可达性分析算法
2. 垃圾回收算法
标记清除法
标记整理法
复制算法
3. 分代垃圾回收机制
4. 垃圾回收器
5. 四种引用
类加载
类加载器的分类
类加载过程
LoadClass和forName的区别
双亲委派机制
自定义类加载器
public class MyClassLoader extends ClassLoader { private String path; private String classLoaderName; public MyClassLoader(String path, String classLoaderName) { this.path = path; this.classLoaderName = classLoaderName; } //用于寻找类文件 @Override public Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); } //用于加载类文件 private byte[] loadClassData(String name) { name = path + name + ".class"; try (InputStream in = new FileInputStream(new File(name)); ByteArrayOutputStream out = new ByteArrayOutputStream();) { int i = 0; while ((i = in.read()) != -1) { out.write(i); } return out.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; } }
public class MyClassLoaderChecker { public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException { MyClassLoader m = new MyClassLoader("C:\Users\73787\Desktop\","myClassLoader"); Class<?> c = m.loadClass("Robot"); System.out.println(c.getClassLoader()); c.newInstance(); } }
反射机制
反射的定义
反射的常用场景
反射相关的类
Class类:
Filed类
Method类
Constructor类
案例
public class Robot { //私有属性 private String name; //公有方法 public void sayHi(String hello){ System.out.println(hello+" "+name); } //私有方法 private String thorwHello(String tag){ return "hello "+tag; } }
public class ReflectSample { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { //加载类 Class<?> rc = Class.forName("leetcode.Robot"); //获取类实例 Robot r = (Robot)rc.newInstance(); //打印类名 System.out.println(rc.getName()); //加载一个私有方法 Method getHello = rc.getDeclaredMethod("thorwHello",String.class); getHello.setAccessible(true); Object bob = getHello.invoke(r, "bob"); System.out.println(bob); //加载一个公有方法 Method sayHi = rc.getMethod("sayHi",String.class); Object welcome = sayHi.invoke(r,"welcome"); //加载一个私有属性 Field name = rc.getDeclaredField("name"); name.setAccessible(true); name.set(r,"tom"); sayHi.invoke(r,"welcome"); } }
Java内存模型
什么是Java内存模型(JMM)
为什么会有Java内存模型
原子性
可见性
/** * 〈可见性问题分析〉 * * @author Chkl * @create 2020/3/4 * @since 1.0.0 */ public class FieldVisibility { int a = 1; int b = 2; private void change() { a = 3; b = a; } private void print() { System.out.println("b=" + b + ";a=" + a); } public static void main(String[] args) { while (true) { FieldVisibility test = new FieldVisibility(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.change(); } }).start(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.print(); } }).start(); } } }
– 这种情况就是可见性问题
– a的值在线程1(执行交换线程)的本地缓存中进行了更新,但是并没有同步到共享缓存,而b的值成功的更新到了共享缓存,导致线程2(执行打印线程)从共享缓存中获取到的数据并不是实时的最新数据
–有序性(重排序)
计算: a = 3; b = 2; a = a + 1; 重排序优化前的instructions load a set to 3 store 3 load b set to 2 store b load a set to 4 store a 经过重排序处理后 load a set to 3 set to 4 store a load b set to 2 store b 上述少了两个指令,优化了性能
volatile
happens-before规则
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算