使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系 。例如类java.lang.,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此类在程序的各种类加载器环境中都能够保证是同一个类 。反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.的类,并放在程序的中,那系统中就会出现多个不同的类,Java类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱 。(保证程序的安全性,类加载的有规则性)
对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间 。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等 。(确保类的唯一可确定性)
运行时数据区
文章插图
程序计数器
程序计数器()是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器 。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成 。
由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令 。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存 。
如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地()方法,这个计数器值则应为空() 。
此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何情况的区域 。
堆
对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块 。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建 。此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例都在这里分配内存 。在《Java虚拟机规范》中对Java堆的描述是:“所有的对象实例以及数组都应当在堆上分配”,而这里笔者写的“几乎”是指从实现角度来看,随着Java语言的发展,现在已经能看到些许迹象表明日后可能出现值类型的支持,即使只考虑现在,由于即时编译技术的进步,尤其是逃逸分析技术的日渐强大,栈上分配、标量替换优化手段已经导致一些微妙的变化悄然发生,所以说Java对象实例都分配在堆上也渐渐变得不是那么绝对了 。
Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定) 。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出异常 。
? 堆大小 = 新生代 + 老年代。堆的大小可通过参数–Xms(堆的初始容量)、-Xmx(堆的最大容量) 来指定 。
- DHCP与DHCPv6讲解
- 菇娘果的功效与作用
- C++ 单元测试与代码覆盖率测试方法
- 蒲公英泡枸杞子水喝的功效与作用
- 《React Native 精解与实战》书籍连载「React Native 底层
- 【Linux】知识点:线程ID与LWP
- TensorFlow——tensorflow指定CPU与GPU运算
- 二 JVM.垃圾回收算法/策略内存分配
- 冯诺依曼结构与哈佛结构
- JAVA的垃圾收集器与内存分配策略【一篇文章直接看懂】