本文共 8106 字,大约阅读时间需要 27 分钟。
1.Class Loader:依据特定格式,加载class文件到内存
2.Execution Engine:对加载的二进制字节码命令进行解析 3.Native Interface:融合不同开发语言的原生库为Java所用 4.Runtime Data Area:JVM内存空间结构模型(1) 线程私有 程序计数器(字节码指令)、虚拟机栈(java方法)、本地方法栈(native方法) (2) 线程共享 MetaSpace、Java堆
作用:
(1)线程独立拥有计数器,是当前线程所执行的字节码行号指示器(逻辑) (2)改变计数器的值来选取当前线程需要执行的下一条字节码指令 (3)线程独有,即一个线程拥有一个计数器 (4)对Java方法计数,如果是Native方法则计数器值为Undefined (5)不会发生内存泄漏(1)作用:java方法执行的内存模型,包含多个栈帧
(2)程序执行过程:对于当前线程,每个方法会产生1个栈帧,当方法执行结束后,该栈帧取消。而每个栈帧中包含:局部变量表、操作栈、动态链接、返回地址等等。 (3) 当前栈中包含内容 局部变量表:包含了执行过程中的所有变量(boolen,char,string等等) 操作数栈:操作数栈:入栈、出栈、复制、交换、产生消费变量(4) 实例实现当前线程调用过程 测试类:MemoryCodeSample.javapackage com.spring.ioc.c5;public class MemoryCodeSample { public static int add(int a,int b){ int c=0; c=a+b; return c; }}
src\main\java\com\spring\ioc\c5>javap -verbose MemoryCodeSample.class
src\main\java\com\spring\ioc\c5>javap -verbose MemoryCodeSample.class
Classfile …/src/main/java/com/spring/ioc/c5/MemoryCodeSample.class
Last modified 2019-12-11; size 292 bytes MD5 checksum 6d23ead3e45557a26587f2d70fbb9ee5 Compiled from "MemoryCodeSample.java" public class com.spring.ioc.c5.MemoryCodeSample //描述类信息 minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #3.#12 // java/lang/Object."<init>":()V #2 = Class #13 // com/spring/ioc/c5/MemoryCodeSample #3 = Class #14 // java/lang/Object #4 = Utf8 <init> #5 = Utf8 ()V #6 = Utf8 Code #7 = Utf8 LineNumberTable #8 = Utf8 add #9 = Utf8 (II)I #10 = Utf8 SourceFile #11 = Utf8 MemoryCodeSample.java #12 = NameAndType #4:#5 // "<init>":()V #13 = Utf8 com/spring/ioc/c5/MemoryCodeSample #14 = Utf8 java/lang/Object { public com.spring.ioc.c5.MemoryCodeSample(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0public static int add(int, int);
descriptor: (II)I // 接受两个int输入,返回也是一个int flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=2 // 栈容量2,本地变量3,参数2个 0: iconst_0 // 把0压入操作数栈顶;同时获取输入的局部变量(1,2) 1: istore_2 // 把操作数栈顶元素取出,放入局部变量temp[2]位置 2: iload_0 // 把局部变量temp[0]压入操作数栈顶 3: iload_1 // 把局部变量temp[1]压入操作数栈顶 4: iadd // 把操作数栈中元素加和,压入操作数栈顶 5: istore_2 // 把操作数栈顶元素取出,放入局部变量temp[2] 6: iload_2 // 把局部变量temp[2]压入操作数栈中 7: ireturn // 把操作数栈顶元素返回 LineNumberTable: line 6: 0 //代码第6行,对应字节码第0行 line 7: 2 //代码第7行,对应字节码第2行 line 8: 6 //代码第8行,对应字节码第6行 } SourceFile: "MemoryCodeSample.java"具体过程add(1+2)实例
与虚拟机栈类似,主要用于标注native方法
(1) 作用:存储class基本信息,包括java对象的method和field等
(2) 元空间使用的是本地内存,而永久代是使用jvm内存 (3) 元空间(MetaSpace)与永久代(PermGen)区别 在Java8中,元空间(Metaspace)登上舞台,方法区存在于元空间(Metaspace)。同时,元空间不再与堆连续,而且是存在于本地内存(Native memory)。 参考:https://www.cnblogs.com/secbro/p/11718987.html(1)堆内存分类:根据对象存活的周期不同,把堆内存划分为:新生代、老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存分代策略。
(2)作用:堆内存是虚拟机管理的内存中最大的一块,也是垃圾回收最频繁的一块区域,我们程序所有的对象实例都存放在堆内存中。 给堆内存分代是为了提高对象内存分配和垃圾回收的效率。试想一下,如果堆内存没有区域划分,所有的新创建的对象和生命周期很长的对象放在一起,随着程序的执行,堆内存需要频繁进行垃圾收集,而每次回收都要遍历所有的对象,遍历这些对象所花费的时间代价是巨大的,会严重影响我们的GC效率。 有了内存分代,新创建的对象会在新生代中分配内存;经过多次回收仍然存活下来的对象存放在老年代中;静态属性、类信息等存放在永久代中。新生代中的对象存活时间短,只需要在新生代区域中频繁进行GC;老年代中对象生命周期长,内存回收的频率相对较低,不需要频繁进行回收;永久代中回收效果太差,一般不进行垃圾回收。还可以根据不同年代的特点采用合适的垃圾收集算法。分代收集大大提升了收集效率,这些都是内存分代带来的好处。(2)Java堆的分配区域
参考:
元空间和永久代之前区别
元空间并不在虚拟机中而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。类的元数据放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少类的元数据就不再由MaxPermSize控制,而由系统的实际可用空间来控制。
1.递归由于调度栈过深,导致超过虚拟机栈帧深度会引发java.lang.StackOverflowError异常
(1)代码:斐波那契数列
package com.spring.ioc.c5;public class Fibonacci { public static int fibonacci(int n){ if(n==0) return 0; if(n==1) return 1; return fibonacci(n-1)+fibonacci(n-2); } public static void main(String[] args) { System.out.println(fibonacci(1)); System.out.println(fibonacci(2)); System.out.println(fibonacci(3)); System.out.println(fibonacci(4)); System.out.println(fibonacci(5)); System.out.println(fibonacci(1000000)); }}
报错:
1 1 2 3 5Exception in thread "main" java.lang.StackOverflowError(2)解决:限制递归次数,或者使用循环替换递归
2.内存不够导致java.lang.OutOfMemoryError问题
(1)代码
package com.spring.ioc.c5;public class stackLeak { public void stackLeakByThread(){ while (true){ new Thread(){ public void run(){ while (true){ } } }.start(); } }}
(2)报错:(警告:绝对不建议尝试,可能引起死机)
Exception in thread "main"java.lang OutOfMemoryError: unable to create new native thread(1)参数
Xms:设置初始分配大小,默认为物理内存的“1/64”
Xmx:最大分配内存,默认为物理内存的“1/4” -XX:+PrintGCDetails 输出详细的GC处理日志(2)实例
-》代码
package com.algorithm.learn.test;import java.util.Random;public class test1 { public static void main(String[] args) { String str="hello world"; while (true){ str+=str+new Random().nextInt()+new Random().nextInt(88888); } }}
-》参数:-Xms8m -Xmx8m -XX:+PrintGCDetails
-》结果
[GC (Allocation Failure) [PSYoungGen: 1443K->481K(2048K)] 1443K->713K(7680K), 0.0052305 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 1605K->511K(2048K)] 1837K->1015K(7680K), 0.0009143 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 1990K->288K(2048K)] 3452K->1990K(7680K), 0.0008724 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 1283K->0K(2048K)] [ParOldGen: 5538K->1561K(5632K)] 6822K->1561K(7680K), [Metaspace: 3112K->3112K(1056768K)], 0.0068044 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 1024K->96K(2048K)] 4503K->3574K(7680K), 0.0004818 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 96K->64K(2048K)] 3574K->3542K(7680K), 0.0003641 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 64K->0K(2048K)] [ParOldGen: 3478K->3480K(5632K)] 3542K->3480K(7680K), [Metaspace: 3125K->3125K(1056768K)], 0.0090112 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 3480K->3480K(7680K), 0.0002866 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 3480K->3464K(5632K)] 3480K->3464K(7680K), [Metaspace: 3125K->3125K(1056768K)], 0.0078615 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:3332) at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:647) at java.lang.StringBuilder.append(StringBuilder.java:208) at com.algorithm.learn.test.test1.main(test1.java:30) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)Heap PSYoungGen total 2048K, used 57K [0x00000000ffd80000, 0x0000000100000000, 0x0000000100000000) eden space 1536K, 3% used [0x00000000ffd80000,0x00000000ffd8e6f8,0x00000000fff00000) from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) ParOldGen total 5632K, used 3464K [0x00000000ff800000, 0x00000000ffd80000, 0x00000000ffd80000) object space 5632K, 61% used [0x00000000ff800000,0x00000000ffb621a8,0x00000000ffd80000) Metaspace used 3194K, capacity 4494K, committed 4864K, reserved 1056768K class space used 348K, capacity 386K, committed 512K, reserved 1048576K
1.面试问题:你了解Java内存结构么(Java7、8、9内存结构的区别)