设为首页 加入收藏

TOP

Java内存区域与虚拟机类加载机制(一)
2018-03-02 06:57:15 】 浏览:243
Tags:Java 内存 区域 虚拟 加载 机制

一、Java运行时数据区域 



1、程序计数器


  “线程私有”的内存,是一个较小的内存空间,它可以看做当前线程所执行的字节码的行号指示器。Java虚拟机规范中唯一一个没有OutOfMemoryError情况的区域。


  字节码解释器工作时就说通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。


2、Java虚拟机栈


  Java 虚拟栈,线程私有的,它的生命周期与线程相同。每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。


  通过人们所说的“栈”就说虚拟机栈,或说是虚拟机栈中的局部变量表部分。


  这个区域有两个异常:
     ① 如果线程请求的栈深度大于虚拟所允许的深度,将抛StackOverflowError异常;
     ② 虚拟机栈可以动态扩展,但扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。


3、本地方法栈


  本地方法栈作用与虚拟机栈相似,区别在于虚拟机栈为虚拟机执行java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务,线程私有
  Native方法常用的两种请求:
    ① 在方法中调用一些不是有java语言写的代码;
    ② 在方法中用java语言直接操作计算机硬件;
  异常:StackOverflowError、OutOfMemoryError


4、Java堆(Java Heap)


  Java堆是Java虚拟机所管理的内存中最大的一块。在虚拟机启动时创建,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
  Java堆是垃圾收集器管理的主要区域,因此也称为“GC堆”;
  如果在堆中没有内存完成实例分配,并且堆也无法扩展时,将会抛出OutOfMemoryError异常。


5、方法区


  方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。


 二、对象的创建


  1、虚拟机遇到一条new指令时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载,解析和初始化过,如果没有则必须执行相应的类加载过程。


  2、在类加载检查通过后,接下来虚拟机将新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划出来。


  3、内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头)。


  4、虚拟机要对对象进行必要的设置,主要针对对象头的设置。


  5、从虚拟机的角度来看,一个新的对象已经产生了,但从Java查询的视角来看,对象创建才刚刚开始—— <init>方法还没执行,所有的字段都还为零。


     执行完new指令之后会接着执行<init>方法,把对象按照程序员的意愿进行初始化。这样一个真正的对象才算完全产生出来。


三、对象的内存布局


  对象在内存中存储的布局可以分为3块局域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。


  实例数据:对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。


  对齐填充:并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。


四、对象的访问定位


  建立对象是为了使用对象,我们的Java程序需要通过栈上的reference数据来操作堆上的具体对象。目前主流的访问方式有使用句柄和直接指针两种。


  1、如果使用句柄访问的话,那么Java堆中将会划出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型各自的具体地址信息。



  2、如果通过直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象的地址。(Sun HotSpot的实现方式)



 


五、虚拟机类的加载机制


  (一)、类加载的时机


  1、类的生命周期:类从被加载到虚拟机内存中开始,到卸载出内存为止。



 


  图中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的开始,而解析阶段则不一定。


  2、需立即对类进行“初始化”(而加载、验证、准备自然需要在此之前开始)的有且只有的5种请求:


    ① 遇到new、gerstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。


    如:使用new关键字实例化对象时候,读取或设置一个类的静态字段(被final修饰,已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。


    ②使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。


    ③当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。


    ④当虚拟机启动时,用户需要指定一个要执行的主类(包括main()方法的那个类),虚拟机会先初始化这个主流。


    ⑤当使用JDK1.7的动态语言支持时。


  (二)、类加载的过程


  类加载的全过程:加载、验证、准备、解析和初始化这五个阶段。  


  1、加载


  “加载”是“类加载”过程的一个阶段。 


   ① 通过一个类的全限定名来获取定义此类的二进制字节流(通过类加载实现);


     ② 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;


   ③ 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。


   2、验证


  连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。


  大致完成4个阶段的检验动作:文件格式验证、元数据验证、字节码验证、符号引用验证。


  3、准备


  正式为变量分配内存并设置类变量初始值得阶段,这些变量所使用的内存都在方法区中进行分配。首先这个时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中,其次,这里所说的“初始值”通常情况下是数据类型的零值。


  4、解析


  解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。


  在16个用于操作符号引用的字节码指令之前,先对它们所使用的符号引用进行解析。所有虚拟机实现可以根据需要来判断到底是在类被加载器加载时就对常量池中的符号引用进行解析,还是等到一个符号引用将要被使用之前才去解析它。


  解析动作主要针对类或接口、字段、类方法、方法类型、方法句柄和调用点限定符7类符号引用进行解析。


  5、初始化  


  初始化阶段是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说是字节码)。


  在准备阶段,变量已经赋过一次系统要求的初始值,而在初始阶段,则根据查询员通过查询制定的主观计划去初始化变量和其他资源,换而言之,初始化阶

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇CSS3动画特效图文详解(附代码) 下一篇Python urllib urlretrieve函数解..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目