共计 2795 个字符,预计需要花费 7 分钟才能阅读完成。
java 的长处:
- 解脱了硬件平台解放,实现了一次编写,到处运行的现实
- 提供了一个绝对平安的内存治理和拜访机制,防止了绝大多数的内存透露和指针越界
- 实现了热点代码检测和运行时编译及优化,使得 java 利用能随着运行工夫的减少而取得更高的性能
- 有一套欠缺的利用程序接口,还有有数商业机构和开源社区的第三方类库
第一局部
Java 技术体系
- java 程设计语言
- 各种硬件平台的 java 虚拟机
- Class 文件格式
- java api 类库
- 第三方 java 类库
JDK:java 程序设计语言,java 虚拟机和 java api。是反对 java 程序开发的最小环境。
JRE:把 java api 类库重的 java SE api 子集和 java 虚拟机这两局部统称为 jre。Jre 是反对 java 程序经营的规范环境
第二局部 主动内存治理
2 运行时数据区域
2.1 程序计数器
能够看作是以后线程所执行的字节码行号指示器
如果线程正在执行的是一个 java 办法,这个计数器记录的是正在执行的虚拟机字节码的指令地址;如果正在执行的是 Native 办法,这个计数器值为空(Undefined)。此内存区域是惟一一个在 java 虚拟机标准中没有规定任何 OOM 状况的区域
2.2 虚拟机栈
线程公有的,生命周期与线程雷同。
虚拟机栈形容的是 java 办法执行的内存模型:每个办法在执行时都会创立一个栈帧。用于存储局部变量表、操作数栈、动静链接、办法进口等信息。
局部变量表寄存了编译器可知的各种根本类型(boolean、byte、char、short、int、float、long、double)对象援用(refrence 类型,他不等同于对象自身,可能是一个执行对象起始地址的援用指针,也可能是只想一个代表对象的句柄或其余与此对象相干的地位)和 returnAddress 类型(指向了一条字节码指令的地址)
局部变量表所须要的内存空间在编译期间实现调配,当进入一个办法时,这个办法须要在帧中调配多大的局部变量空间是齐全确定的。
2.3 本地办法栈
本地办法栈与虚拟机栈施展的作用相似,不过虚拟机栈为虚拟机执行 java 办法(也就是字节码)服务,本地办法栈则为虚拟机应用到的 Native 办法服务。
在虚拟机标准中办法应用的语言、应用的形式与数据结构并没有强制规定,因而具体的虚拟机能够自在实现。
本地办法栈与虚拟机一样也会跑出 StackOverflowError 和 OutOfMemoryError
2.3java 堆
java 堆是被虽有线程共享的一块内存区域。
此区域的惟一目标就是寄存对象实例。简直所有的对象实例都在这里分配内存。
- 所有的对象实力以及数组都要在堆上调配,然而随着 JIT 编译器的倒退和陶艺剖析技术逐步成熟,栈上调配、标亮替换优化技术将会导致一些奥妙的变动,所有的对象都调配在堆上也慢慢变得不那么相对了。
因为当初的收集器根本都采纳粉黛手机算法,所以 Java 堆还能够细分为:新生代和老年代。
再粗疏一点:
- Eden
- From Survivor
- To Survivor
- Old
- Permanent(对于 HotSpot)
如果在堆中没有内存实现实例调配,并且堆也无奈再扩大时,将会抛出 OutOfMemoryError 异样
2.5 办法区
(Method Area)与堆一样,是各个线程共享的内存区域。用于存储曾经被虚拟机记录的类信息、常量、动态变量、即时编译器编译后的代码等数据。尽管 Java 虚拟机标准把办法区形容为堆的一个逻辑,然而它却有一个别名叫做 Non-heap,目标应该是与 Java 堆辨别开
仅对于 Hotspot 虚拟机来说,存在永恒代这个概念。仅仅是因为 HotSpot 虚拟机设计团队抉择把 GC 分代手机扩大至办法去,或者应用永恒代实现办法区而已。对于其余虚拟机则不存在永恒代。
对这一区域的内存回收次要是针对常量池的回收和对类型的卸载。
当办法去无奈满足内存调配需要时将抛出 OOM 异样
2.6 运行时常量池
(Runtime Constant Pool)是办法区的一部分。Class 文件中除了有类的范本、字段、办法、接口信息等、还有一项是常量常量池(Constant Pool Table),用于寄存编译期生成的各种字面量和符号援用。
运行时常量池绝对于 Class 文件常量池的另外一个重要特色是具备动静个性,Java 语言并不要求常量肯定只有编译期能力缠身,也就是并非预置入 Class 文件中常量池的内容能力进入办法区是进入常量池。运行期间可能将新的常量放入池中。
当常量池无奈再申请到内存是会抛出 OutOfMemoryError
2.7 间接内存
间接内存(Direct memory)并不是虚拟机运行时数据区的一部分,也不是 Java 虚拟机标准中定义的区域。然而这部分内存被间接被频繁应用,也可能导致 OutOfMemoryError
在 JDK1.4 中新退出了 NIO 类,引入了一种基于通道(Channel)与缓冲区(BUffer)的 I / O 形式,它能够应用 Natice 函数间接调配堆外内存,而后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的援用进行操作。这样能在一些场景中进步性能,因为防止了 Java 堆和 Native 对中的来回复制数据。
3 HotSpot 虚拟机探秘
3.1 对象的创立
虚拟机遇到一条 new 指令时
- 查看这个指令的参数是否能在常量池中定位一个类的符号援用,并且查看这个符号援用代表的类是否曾经被加载、解析和初始化过。如果没有,则必须执行相应的类加载过程
- 虚拟机将为新生对象分配内存。对象所需的大小在类加载实现后便能够确定。
对于频繁创建对象的状况,并发时,并不是线程平安的。可能呈现正在给对象 A 分配内存,还没来得及批改,对象 B 又同时应用了原来的指针来分配内存。解决这个问题有两种计划
1、对于分配内存空间的动作同步解决——实际上,虚拟机采纳 CAS 配上失败重试保障更新操作的原子性
2、另一种是把内存调配的动作依照线程划分在不同的空间之中进行,没喝线程在 Java 堆中事后调配一小块内存,成为本地线程调配缓冲(Thread Local Allocation Buffer,TLAB)能够通过 -XX:+/-UseTLAB 参数来设定
- 内存调配完后,虚拟机须要将调配到的内存空间都初始化为零值(不包含对象头)
- 虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、如何能力找到类的元数据信息、对象的哈希码、对象在 GC 分代年龄等信息。这些信息寄存在对象头中。依据虚拟机以后的运行状态不同,如是否应用偏差锁等,对象头会有不同的设置形式
- 下面的工作都实现后,从虚拟机的视角来看,一个新的对象曾经缠身了,然而从 Java 程序的角度来看,对象的创立才刚刚开始——<Init> 办法还没有执行,所有的字段都还是零。所以,一般来说(由字节码中是否追随有 invokespecial 指令所决定),执行 new 指令之后会接着执行 <init> 办法,把对象进行初始化。这样才真正可用的对象才齐全产生进去