一、 相关背景
Java技术体系图:
RIA 富客户端应用
JavaFX:用于构建富互联网应用程序的Java库
Java Web Start 是帮助客户机端应用程序开发,它的独特之处在于关心客户机是如何启动的(从 Web 浏览器或是桌面)中解放出来。
另外,该技术提供了一个使 Web 服务器能独立的分发和更新客户机代码的集合部署方案。
Applet:客户端相关
Java监视和管理控制台
java – jdk – bin – Jconsole.exe
实际程序包:
java – jdk – lib – tools.jar
HotSpot优势:
1.HotSpot VM的热点代码探测能力可以通过执行计数器找出最具有编译价值的代码,然后通知JIT编译器以方法为单位进行编译。
2.如果一个方法被频繁调用,或方法中有效循环次数很多,将会分别触发标准编译和OSR(栈上替换)编译动作。
3.通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,
4.即时编译的时间压力也相对减小,这样有助于引入更多的代码优化技术,输出质量更高的本地代码。
二、Java运行时数据区
线程共享区:线程间共享 方法区与堆
线程独占区:每个线程内部都含有 线程独占区中的 虚拟机栈、本地方法栈、程序计数器
1)程序计数器:
程序计数器(处于线程独占区)是一块较小的内存空间,看做是当前线程执行的 字节码 的行号指示器。
记录内容:
1)如果线程执行的是java方法,这个计数器记录的是正在执行的虚拟字节码指令的地址。
2)如果线程执行的是native方法,那么这个计数器的值为undefined
2)虚拟机栈(包含栈帧):
虚拟机栈描述的是Java方法执行的动态内存模型。
每个方法执行时都会创建一个栈帧。
每个方法从调用到执行完成,对应着一个栈帧在虚拟机栈中 从入栈到出栈的过程
栈帧(包含 局部变量表,操作数栈,动态链接,方法出口)
存储了 局部变量表,操作数栈,动态链接,方法出口。
每个方法从调用到执行完成,对应着一个栈帧在虚拟机栈中 从入栈到出栈的过程
局部变量表、操作数栈、动态链接、静态链接
1)局部变量表:存放了编译器可知的各种基本数据类型、引用类型、returnAddress返回地址类型
2)操作数栈:
1)可理解为java虚拟机栈中 被计算数据的临时存储区。
2)虚拟机把操作数栈作为它的工作区---大多数指令都要从这里弹出数据,执行运算,然后把结果压回操作数栈
相同点:操作数栈与局部变量表一样,均以字长为单位的数组。二者的内存空间在编译期完成分配。
不同点:局部变量表使用索引访问,操作数栈是弹栈/压栈来访问
3)动态链接的全局作用:
1)编译Java程序的时候,会得到程序中独立的各个类或者接口的class文件,看上去毫无关联,
但是他们之间通过接口(harbor)符号互相联系,或者与Java API的class文件相联系
2)运行程序的时候,Java虚拟机装载程序的类和接口,在动态链接的过程中把它们互相勾连起来
动态连接是一个将符号引用 解析为 直接引用的过程。
背景:当java虚拟机执行字节码时,如果它遇到一个操作码,这个操作码第一次使用一个指向另一个类的符号引用 。
那么虚拟机就必须解析这个符号引用。
解析:在解析时,虚拟机执行两个基本任务
1.查找被引用的类,(如果必要的话就装载它)
2.将符号引用替换为直接引用,这样当它以后再次遇到相同的引用时,它就可以立即使用这个直接引用,而不必花时间再次解析这个符号引用了。
4) 静态链接:
解析调用一定是个静态过程,在编译期间就完全确定,在类加载的解析阶段就会把涉及的符号引用转化为可确定的直接引用,、
不会延迟到运行期再去完成
3)本地方法栈:与虚拟机栈作用相似。
区别:
虚拟机栈 为虚拟机执行Java方法服务,
本地方法栈:为虚拟机执行native方法服务
注:hotspot虚拟机中,虚拟机栈与本地方法栈是一体的
4) Java堆:
1)存放对象的实例
2)垃圾收集器管理的主要区域
3)堆细分为,新生代、老年代。
再细分为 Eden、From Survivor、To Survivor等
堆内存扩展:通过 -Xmx -Xms 控制
5) 方法区 (包含运行时常量池)
1)存储虚拟机加载的类信息、常量、静态常量、即编译后的代码等数据
类信息:类的版本、字段、方法、接口等
2)方法区中的垃圾回收目标 主要为 常量池的回收与类型的卸载
运行时常量池:
方法区的一部分 用于存储编译器生成的字面量和符号引用。
存储内容在类加载后存放
直接内存:
直接内存并不是虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中宏定义的内存区域。
在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O 方式,它可以使用native 函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer 对象作为这块内存的引用进行操作。
这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
直接内存大小 受本机总内存大小限制,不会受到Java 堆大小的限制,
配置虚拟机参数时,不要忽略直接内存 防止出现OutOfMemoryError异常
三、对象的创建
创建步骤:
1.new类名
2.根据new的参数在常量池中定位一个类的符号引用
3.如果没有找到这个符号引用,说明类还没有被加载,则进行类的加载、解析和初始化
4.虚拟机为对象分配内存(位于堆中)
5.将分配的内存化为零值(不包括对象头)
6.调用对象的init方法
内存分配:
两种方式:指针碰撞和空闲列表。
由Java堆是否规整决定 使用哪种方式。
由垃圾回收策略决定 Java堆是否规整。
指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存都放一边,空闲的内存放另一边,中间放着一个指针作为分界点的指示器,所分配内存就仅仅是把哪个指针向空闲空间那边挪动一段与对象大小相等的举例,这种分配方案就叫指针碰撞
空闲列表:有一个列表,其中记录中哪些内存块可用,在分配的时候从列表中找到一块足够大的空间划分给对象实例,然后更新列表中的记录。这就叫做空闲列表
线程安全性问题:
问题产生:空闲列表中一块内存被分配后,还未更新空闲列表时,另一对象请求分配该地址。线程不安全
解决:
1)线程同步处理
2)TLAB:(Thread Local Allocation Buffer)本地线程分配缓冲。
对每个线程分配各自的内存区,使每个线程操作不同的区域。
当线程的TLAB用完时,进行线程同步处理
四、 对象的结构:
三部分: Header、InstanceData、Padding
Header(对象头):
1)存放自身运行时数据: 哈希值、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳
2)类型指针:虚拟机通过该指针确定对象是哪个类的实例
InstanceData(实例数据):对象存储的有效信息,即字段内容
Padding(对齐填充):占位符。Java要求对象大小为8字节的整数倍,当数据没有对齐时,使用对齐填充
对象的访问定位:
两种方式:使用句柄、直接指针
句柄:Java堆中划分出一块内存来作为句柄池,引用中存储对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。
句柄优点:引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而引用本身不需要修改。
指针:引用中直接存储对象地址
指针优点:速度更快,节省了一次指针定位的时间开销。由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。