JVM内存模型和类加载机制

Stella981
• 阅读 796

JVM内存模型

Java代码是运行在Java虚拟机(JVM)上的,Java虚拟机通过解释执行(解释器)或编译执行(编译器)来完成。

Java内存模型分为5个部分:方法区(Method Area),Java堆(Heap),Java栈(VM Stack),本地方法栈(Native Method Stack),程序计数器(PC 寄存器)

JVM内存模型和类加载机制 (图片来源:http://gityuan.com/images/jvm/jvm\_memory\_1.png)

线程共享区:

方法区(Method Area):方法区是各个线程共享的区域,存放类信息,常量,静态常量,编译器编译后的代码等信息。

Java堆(Heap):Java堆也是线程共享区域,类的实例存放在这里,一个系统会产生很多Java实例,因此Java堆的空间是最大的,如果Java堆的空间不足,就会抛出OutOfMemoryError异常。

线程私有区:

Java栈(VM Stack):线程私有区域,生命周期与线程相同,一个线程对应一个Java栈,每执行一个方法就会向栈里压一个元素,这个元素叫“栈帧”,栈帧中包含了方法中保存了该方法调用的参数、局部变量和返回地址等信息,如果栈空间不足了就会抛出StackOverflowError异常。

本地方法栈(Native Method Stack):和Java栈类似,本地方法栈是用来执行本地方法的,存放的方法调用本地方法接口,最终调用本地方法库,实现与操作系统,硬件交互的目的。

程序计数器:这里对应的类以及加载,实例对象,方法,静态变量去了该去的地方,那么问题来了,程序该怎么执行,哪个方法先执行,哪个方法后执行,这些指令执行的顺序就是PC寄存器在管,它的作用就是控制程序指令的执行顺序。

类加载机制

 编写的java代码会通过编译器编译成字节编码的.class文件,再把字节编码加载到JVM中,映射到内存的各个区域中,程序就可以在内存中运行了。

类加载流程

JVM内存模型和类加载机制

(图片来源:https://images2017.cnblogs.com/blog/352511/201708/352511-20170825174319746-900347526.png)

1,加载

加载是类装载的第一步,内存中生成一个代表这个类的java.lang.class对象,通过class文件的路径读取到二进制流,并解析二进制里的元数据(类型,常量等),作为方法区这个类的各种数据量的入口;这里的不一定从class文件获取,这里既可以从ZIP包(jar,war)包中获取,也在运行时动态生成(jsp转换成class文件,动态代理生成)。

2,连接

连接又可分为验证,准备,解析。

2.1,验证

验证主要是判断class文件的合法性,对版本号进行验证(例如如果使用java1.8编译后的class文件要再java1.6虚拟机上运行),还会对元数据,字节编码等进行验证,确保class文件里的字节流信息符合当前虚拟机的要求,不会危害虚拟机的安全。

2.2,准备

准备主要是分配内存,为变量分配初始值,即在方法区中分配这些变量所使用的内存空间,例如:

public static int i = 1;

在准备阶段i的值会被初始化为0,后面的类的初始化阶段才会赋值为1;

public static final int i = 1;

对应常量(static final)i,在准备阶段就会被赋值1;

2.3,解析

解析就是把代码中的符号引用替换为直接引用;例如某个类继承了java.lang.Object,原来的符号引用记录的是“java.lang.Object”,并不是java.lang,Object对象,直接引用就是找出对应的java.lang.Object对应的内存地址,建立直接引用关系;

3,初始化

初始化的过程包括执行类构造器方法,static变量赋值语句,static{}代码块,如果是一个子类进行初始化会先对其父类进行初始化,保证其父类在子类之前进行初始化;所以其实在java中初始化一个类,那么必然是先初始化java.lang.Object,因为所有的java类都继承自java.lang.Object。

以下几种情况不会执行类初始化:

  • 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
  • 定义对象数组,不会触发该类的初始化。
  • 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
  • 通过类名获取Class对象,不会触发类的初始化。
  • 通过Class.forName加载指定类时,如果指定参数initialize为false时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
  • 通过ClassLoader默认的loadClass方法,也不会触发初始化动作。

类加载器

在JVM中有三中类加载器,BootStrap Classloader(启动Classloader)、Extension Classloader(扩展Classloader)和APP Classloader(应用Classloader);

BootStrap ClassLoader主要加载JVM自身需要的类,这个加载器由C++编写是虚拟机的一部分,负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。

Extension Classloader是sun.misc.Launcher中的内部类ExtClassLoader,负责加载 JAVA_HOME\lib\ext 目录中的,或通过java.ext.dirs系统变量指定路径中的类库。

APP ClassLoader是sun.misc.Launcher中的内部类AppClassLoader,负责加载用户路径上的类库。

用户也可以通过继承ClassLoader实现自己的类加载器。

双亲委派模式

当一个类加载器接收到类加载的任务时,会首先交给其父类加载器去加载,只有当父类加载器无法加载是其才会自己加载。其好处是可以避免一个类被重复加载。

即使两个类来源于相同的class文件,如果使用的类加载器不同,加载后的对象时完全不同的,这个不同反应在对象的 equals()、isAssignableFrom()、isInstance()等方法的返回结果,也包括了使用 instanceof 关键字对对象所属关系的判定结果。

双亲委派模式的问题

顶层ClassLoader,无法加载底层ClassLoader的类

Java框架(rt.jar)如何加载应用的类?

比如:javax.xml.parsers包中定义了xml解析的类接口
Service Provider Interface SPI 位于rt.jar 
即接口在启动ClassLoader中。
而SPI的实现类,在AppLoader。

这样就无法用BootstrapClassLoader去加载SPI的实现类。

解决

JDK中提供了一个方法:

1:  Thread. setContextClassLoader()

用以解决顶层ClassLoader无法访问底层ClassLoader的类的问题;
基本思想是,在顶层ClassLoader中,传入底层ClassLoader的实例。

Reference:http://www.cnblogs.com/leefreeman/p/7429112.htmlhttp://www.importnew.com/25295.html

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java基础知识随身记
2018年11月12日20:51:35一、基础知识:1、JVM、JRE和JDK的区别:JVM(JavaVirtualMachine):java虚拟机,用于保证java的跨平台的特性。  java语言是跨平台,jvm不是跨平台的。JRE(JavaRuntimeEnvironment):java的运行环境,包括jvmjava的核心类
Stella981 Stella981
3年前
JVM内存区域划分
JVM内存区域划分一、JVM运行时数据区划分根据《Java虚拟机规范》JVM会把它管理的内存划分为若干个不同的数据区域,如下图所示:方法区、堆、栈(虚拟机栈、本地方法栈)、程序计数器。线程私有的意思是指,JVM每遇到一个新的线程就会为他们分配栈和程序计数器。!(https
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
JVM运行机制(非原创)
文章大纲1.JVM基本概念2.JVM的体系结构3.JVM启动流程一、JVM基本概念1.Java虚拟机(JVM)是可运行Java代码的假想计算机2.Java虚拟机包括类加载器、一组寄存器、方法区、一个垃圾回收堆、直接内存、一个栈、和一个存储方法域、PC寄存器等3.Java编译、运行流程如
Stella981 Stella981
3年前
JVM的基础知识点Java的内存模型
阅读文本大概需要3分钟。Java虚拟机是Java工程师必学的进阶功课,这段时间开始死磕JVM。今天梳理一下JVM的基础知识点Java的内存模型!!(https://oscimg.oschina.net/oscnet/d48bb92f83f6e209089d8c03dc2ba35cf45.png)程序计数器是什么:程序计数器是
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了