1.类加载子系统
在Java虚拟机中,负责查找并装载类的部分称为类装载子系统,类装载器子系统用于定位和加载编译后的class文件;
好处:
- Java中的类随着它的类加载器一起具备了了一种带有优先级的层次关系。例如java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于顶端的启动类加载器进行加载,因此Object在程序的各种类加载器环境中都能保证是同一个类。
- 反之,如果没有双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.Object类,并放在程序的ClassPath中,那系统就会出现多个不同的Object类,Java类型体系中最基础的行为也无法保证,程序会变得混乱。
类加载过程
加载
验证(文件格式验证(魔数,版本号,常量),元数据验证,字节码验证,符号引用验证)
准备(为静态变量分配内存,并将其初始化为初始值)
解析(将符号引用替换为直接引用。类和接口、方法、接口方法、字段等解析)
初始化(执行clinit()方法)
类初始化的顺序
父类静态变量
父类静态代码块
子类静态变量
子类静态代码块
父类普通变量
父类普通代码块
父类构造函数
子类普通变量
子类普通代码块
子类构造函数
类的初始化时机:www.cnblogs.com/fnlingnzb-l…
2.JVM内存部分(运行时数据区)
JVM在运行时会把它所管理的内子划分为若干不同的数据区域,宏观上划分为两大块:
- 线程私有数据区
- 线程公有数据区
2.1线程私有数据区(3个区域),随着线程结束而结束,不会GC回收
2.1.1程序计数器
- 1、线程私有;
- 2、一块较小的区域;
- 3、记录程序执行位置、行号;
- 4、不存在OutOfMemoryError内存溢出;
- 5、无GC回收;
2.1.2虚拟机栈
虚拟机栈是采用了一种栈的数据结构,入口和出口只有一个,分为入栈和出栈,后进先出;
- 局部变量表:是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量;
- 操作数栈:也叫操作栈,它是一个先进后出的栈(FILO),当一个方法刚刚开始执行时,其操作数栈是空的,随着方法执行,会从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是入栈和出栈操作,一个完整的方法执行期间往往包含多个这样入/出栈的过程;
- 动态链接:一个方法要调用其他方法,需要将这些方法的符号引用转化为其在内存地址中的直接引用,而符号引用存在于方法区中的运行时常量池,所以需要在运行时动态将这些符号引用转化为直接引用;
- 返回地址:方法不管是正常执行结束还是异常退出,需要返回方法被调用的位置;
虚拟机栈的特点
- 1、线程私有;
- 2、方法执行会创建栈帧,存储局部变量表等信息;
- 3、方法执行入虚拟机栈,方法执行完出虚拟机栈;
- 4、栈深度大于虚拟机所允许 StackOverflowError栈溢出;
- 5、栈需扩展而无法申请空间 OutOfMemoryError;
- 6、栈里面运行方法,存放方法的局部变量名,变量名所指向的值(常量值、对象值等)都存放到堆上的;
- 7、栈一般都不设置大小,栈所占的空间其实很小,可以通过-Xss1M(如-Xss128k)进行设置,如果不设置默认为1M
- 8、该区域不会有GC回收;
2.1.3本地方法栈
- 1、与虚拟机栈基本类似;
- 2、区别在于本地方法栈为Native方法服务;
- 3、Sun HotSpot将虚拟机栈和本地方法栈合并;
- 4、有StackOverflowError 和OutOfMemoryError;
- 5、GC不会回收该区域;
2.2线程公有数据区(2个区域)
2.2.1方法区(元空间)
- 方法区(jdk1.7后合并到了堆)
- JDK1.8仍然保留方法区的概念,只不过实现方式不同,JDK1.8称为元空间(Metaspace),元空间与堆不相连,但与堆共享物理内存,逻辑上可认为在堆中;
方法区(元空间)的特点
- 1、线程共享;
- 2、存储类信息、常量、运行时常量池、静态变量、即时编译器编译后的代码等数据;
- 3、HotSpot虚拟机上将方法区叫永久代;(1.7及之前的版本)
- 4、垃圾收集很少光顾该区域(无GC回收);
- 5、可通过-XX:MaxPermSize设置最大值;(1.7及之前的版本,1.8是MaxMetaspaceSize)
- 6、空间不够分配时 OutOfMemoryError;
方法区(元空间)溢出
-
1、方法区也称永久代(1.7及之前的版本);
-
2、方法区存放class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等,比如通过反射大量生成动态类填充该区域即会发生内存溢出;
-
- JDK1.6及之前:有永久代,常量池在方法区;
-
- JDK1.7:从某个版本开始已去除永久代,常量池1.7放入堆中;
-
- JDK1.8及之后:无永久代,常量池1.8在元空间;
- 所以在jdk1.7及jdk1.8中不会报OutOfMemoryError:PermGen space,但会报OutOfMemoryError:Java heap space,因为jdk1.7和1.8已经没有方法区了,也就是说没有永久代了。
- 1.8是元空间,如此设置其大小:-XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=10M
2.2.2堆
堆的特点
- 1、线程共享
- 2、内存中最大的区域;
- 3、虚拟机启动时创建;
- 4、存放所有实例对象;
- 5、GC垃圾收集器的主要管理区域;
- 6、可分为新生代、老年代;
- 7、更细化可分为Eden、From Survivor、To Survivor,Eden:Survivor=8:1:1
- 8、可通过-Xmx、-Xms调节堆大小;
- 9、无法再扩展OutOfMemoryError
堆溢出
不断创建对象又不释放,当对象到达一定数量,无堆空间将产生堆内存溢出;
- 内存泄漏:GC Roots 到对象之间有可达路径而无法收集;
- 内存溢出:GCRoots到对象之间无可达路径,可以被收集,但对象还需要存活着,此时可根据物理机内存适当调大虚拟机堆参数-Xms、-Xmx,分析代码是否对象生命周期是否过长、对象是否持有状态时间过长;
注意
类成员变量和对象成员变量在内存中存放情况 blog.csdn.net/FightingITP…