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>办法,把对象进行初始化。这样才真正可用的对象才齐全产生进去