`
文昌平蓝杰
  • 浏览: 54785 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

JVM类加载和垃圾回收

    博客分类:
  • JVM
阅读更多

一、JAVA跨平台

    JVM(Java Virtual Machine)意思是java虚拟机,所有的java程序,都运行在这个虚拟机上,也因此java拥有了诞生之初最大的优势,[b]跨平台[/b]。java文件经过编译成为class文件,虚拟机识别class文件,并运行。程序并不直接与操作系统进行交互,所以java可以跨平台。虚拟机起到了中间作用,虚拟机本身不是跨平台的,所以我研究一下java虚拟机的原理,究竟是如何工作的。

 

二、类加载

    类加载器一共分四种,引导类加载器,扩展类加载器,系统类加载器,自定义类加载器。他们之间有层级关系,具体关系如下图所示(图片来自于网络)

类加载结构

        java在类加载过程中,检查加载是从下至上的,而加载过程是从上之下的。当一个类加载器接收到加载请求的时候,首先检查这个类是不是被自己加载过,如果没有,则提交给上级加载器去加载。上级加载器重复此过程,直到顶层,顶层发现自己也没有加载过,那么就去尝试加载此类,如果发现自己的加载范围内找不到该类,就会交给下层去加载,如果到最后,连classpath和指定目录都没有找到类,那么类加载器就会爆抛异常。

 

三、类链接和初始化

      在类被加载都系统后,就会进行一些诸如分类内存空间,赋初始值,初始化变量等工作。

       1.类链接

              ①验证:验证被加载后的类是否有正确的结构,类数据是否会符合虚拟机的要求。

              ②准备:分配内存,赋默认值,静态变量赋初始值等

              ③解析:将运行时常量池中的符号引用替换为直接引用(这句话我没懂,所以直接引用别人的)

       2.类初始化

              在一下情况下,类会被初始化

              ①创建类的实例,比如new一个类时

              ②初始化某个类的子类,因为初始化子类的时候,会先初始化父类
              ③反射,如用Class.forName()调用,直接.class不会
              ④ 访问类或接口的静态变量或者静态方法
              ⑤java虚拟机启动时被标明为启动类的类,比如含有Main的启动类

              父类静态成员、静态代码块—>子类静态成员、静态代码块—>父类和子类实例成员内存分配—>父类实例成员、代码块—>父类构造函数—>子类实例成员、代码块—>子类构造函数

 

四、运行时数据区

      在java程序运行的过程中,所有的数据都是存储在“运行时数据区”,比如类加载后,类的基本信息会存储在方法区,初始化某个对象后,对象会被存储在JAVA堆区,调用某个方法时,方法内部变量会存储在JAVA栈区。具体的情况如下:运行时数据区

 

    运行时数据区分为5个大块,方法区,JAVA堆,JAVA栈,本地方法栈,PC计数器。其二者是线程共享的,后三者是线程独享的。

    ①PC计数器:线程执行Java方法时,记录其正在执行的虚拟机字节码指令地址,因为是线程独享,所以在线程相互切换执行时,不会混乱。

    ②本地方法栈:为执行本地方法而服务的栈。

    ③JAVA栈:JAVA栈是线程私有的,一个线程会对应一个JAVA栈,生命周期与线程相同,线程中每调用一个方法,就会JAVA栈就会产生一个栈帧,栈帧存储着方法的局部变量、操作栈、动态链接、方法出口的信息。每一次方法的调用,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程,如果递归调用,那么每一层深度,就会产生一个栈帧。

    ④JAVA堆:JAVA堆区域是线程共享区域,它存储了所有类的实例,是运行时区域中最大的一块。不断有新对象产生,也不断的被垃圾回收给销毁。JAVA堆的大小是可以配置的。

    ⑤方法区:也是线程共享的区域,存储类所有类的类信息,我称之为叫类的结构信息。值得注意的是,类的常量、静态变量也是存储在这一块的。因为静态变量不属于具体的某个对象,而是类本身的一个属性。

 

五、垃圾回收

      垃圾回收主要针对的是JAVA堆区域,在这个区域,要存储每一个类的实例(对象),是一个很庞大的区域。

      当一个对象不再被使用的时候,比如已经无法通过引用去找到的对象。就需要回收这部分内存,以供后边的实例使用。为了确定找到这些没有用的对象并清除掉,产生了一些经典的算法。

          ①引用计数器:创建的对象每多赋值一个变量,计数器就加一,反之就减一。当对象引用为零的时候,他就永远不能被访问了,就应该被清理了。缺点:不能识别闭环引用,假如A-B,B-A,那么AB都有一次引用,就永远不会清理。

          ②标记清除:利用有向图的思想,计算根节点到对象的路径,如果路径为无穷大,这表示对象不可达,应该被清除。缺点:产生内存碎片较多。

          ③标记清除压紧:为了解决②的碎片问题,在②的基础上增加一个操作,把所有存活的对象,都移动并对齐到JAVA堆的一边去。这样就不会产生碎片了,不过效率会更低一点。

          ④复制:把JAVA堆分成2个区域AB,活动对象在A中产生,当A内存满的时候,就扫描A,把A中的存活对象整齐的复制到B中。缺点:消耗内存大

          ⑤新生代老年代:由于大多数对象存活的时间较短,而少部分会存活很久。所以将JAVA堆分为多个区域,每个区域作为一代,如ABC,对象在A中产生,如果A满了就扫描一次,将存活对象复制到B,当B也满了,将B对象复制到C中,由于C里边的对象是存活了2轮的,所以很少会被清除掉,所以可以称之C为老年代,对应A为新生代,B为年轻代。优点:灵活(可以根据程序调整代的数量,代的大小)节省时间(新生代需要扫描次数较多)。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics