天天按照书本学一点,会把本身的总结思考写下来,造成输出,持续更新,立帖为证java
-- 2020年7月7日 开始第一次学习 -- 2020年7月8日 今天在百忙Rush B中抽出时间,学了点习,计划明天把本地方法栈和Java堆看完总结完 -- 2020年7月10日 第一次周五学习,也算是有进步,翻了一下书感受好多啊,不知道何时能看完 -- 2020年7月15日 冲鸭!!!!
Java与C++在内存控制方面大相径庭,由于Java虚拟机有自动内存管理机制,因此Java程序员就牺牲部份内存控制权,来换取编写程序时的便利。虽然不容易出现内存泄漏和内存溢出问题,但仍是有必要学习点Java虚拟机相关知识,除了在遇到虚拟机问题时能够快速解决以外,还能够和别人装逼(最大的快乐!)
Java虚拟机在运行Java程序的时候,会将内存自动划分为不一样区域,不一样区域对应的功能、建立销毁时间也不一样,有些区域会随着虚拟机启动而一直存在,有些区域以来用户的线程启动结束而建立销毁。程序员
内存区域分为如下几个区域:数组
思考:了下为何也是线程私有的?应该时每一个线程执行方法不一样,里面的一些临时变量等也不会相同,为了在切换线程时不会发生混乱互相干扰,因此须要和程序计数器同样,也是线程私有的内存空间缓存
本地方法栈与Java虚拟机栈做用类似,但也稍有区别。Java虚拟机栈是为虚拟机执行Java方法(字节码)服务的,而本地方法栈是为虚拟机执行本地方法(Native)服务的。安全
由于在《Java虚拟机规范》中,并无对本地方法栈作强制规定,因此不一样虚拟机实现的方式可能不一样,有些虚拟机(HotSpot虚拟机)直接将本地方法栈和Java虚拟机栈合二为一多线程
与Java虚拟机栈同样,当本地方法栈深度超出规定(溢出)和栈扩展失败的时候,也会报StackOverflowError和OutOfMemoryError异常函数
Java堆是虚拟机管理内存中最大的一块,被全部线程共享,在虚拟机启动时建立。布局
主要是负责存放对象实例,按照《Java虚拟机规范》描述是:全部对象实例以及数组都应当在堆上分配。考虑到Java语言的发展,和即时编译技术的出现,将来可能会出现对象实例不在堆上分配的状况。性能
Java堆是垃圾收集器管理的内存区域,所以也被称为"GC堆"。垃圾收集器大部分是基于分代收集理论设计的,因此会出现新生代、老年代、永久代,Eden空间、Form Survivor空间、To Survivor空间等名词,这些划分的区域仅仅是垃圾收集器共同特性或设计风格,并不能说是Java堆是由这些区域组成的。学习
从分配内存角度说,线程共享的Java堆能够划分多个线程私有的分配缓冲区(TLAB),划分出来的惟一做用仍是存放对象实例,目的是为了更快更好的分配和回收内存。
Java堆在逻辑上是连续的,但在物理上并不要求连续。若是存放的是大对象,例如:数组对象,大多数虚拟机为了实现简单、存储高效,可能会要求连续的存储空间。
Java堆既能够是固定大小,也能够是可扩展的。目前主流虚拟机都是可扩展的,经过参数-Xmx和-Xms设定。若是在Java堆中没有内存给对象实例分配,而且没法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常。
在《Java虚拟机规范》中对方法区的约束是十分宽松的,许多部分和Java堆相同,例如:
并把方法区描述为堆的一个逻辑部分,可是方法区和堆仍是有区别的,方法区的另外一个别名叫"非堆(Non-Heap)",方法区用来存放已经被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
方法区与永久代关系
本质上二者并非等价的,但不少人将二者混为一谈,这是由于当初HotSpot虚拟机在设计的时候,为了简单方即可以像管理Java堆同样管理这部份内存,将垃圾收集器的分代设计扩展至方法区,即便用永久代来实现方法区。但Java虚拟机规范中并无对方法区的实现作具体要求,因此其余虚拟机(如:BEA的JRockit、IBM的J9)都没有永久代这个概念。
使用永久代实现方法区好处:
能够像管理Java堆同样管理一部份内存,省去了专门为方法区编写管理代码的工做
使用永久代实现方法区坏处:
会致使Java应用更容易遇到内存溢出的问题,永久代有-XX:MaxPermSize的上限,即便没有设置也有默认值,而J9和JRockit只要没有触碰到进程可用内存的上限,例如32位系统中4GB限制,就不会出现问题。
有极少数方法(String::intern())会因永久代的缘由而致使不一样虚拟机下有不一样表现
永久代介绍
垃圾收集行为在永久代不多出现,但并非数据进入永久代以后就永久存在了,这一区域内存回收目的主要是针对常量池回收和对类型的卸载,可是由于回收条件严格,因此回收效果总不能使人满意。
当方法区没法知足新内容内存分配的时候,就会抛出OutOfMemoryError异常。
运行时常量池是方法区的一部分,用来存放编辑时生成的各类字面值和符号引用,由于《Java虚拟机规范》并无对这部分作详细要求,因此虚拟机开发者能够按照本身需求去实现这部份内存。除了上面戳的符号引用外,通常还会将符号引用翻译出来的直接引用也存到运行时常量池中。
具有动态性。并不必定是预置入Class文件中常量池才能进入方法区的运行时常量池,运行期间能够将新的常量放入。
当没法申请到足够内存时,会抛出OutOfMemoryError异常。
直接内存并非虚拟机运行时数据区(上面写的都是)的一部分,也不是《Java虚拟机规范》中定义的内存。
用力提升性能,避免在Java堆和Native堆中来回复制数据。
直接内存并不受Java堆内存大小的限制,可是受本机总内存的限制。根据实际内存设置-Xmx等参数时,若是忽略直接内存,可能会致使抛出OutOfMemoryError异常。
类加载:Java虚拟机遇到new指令的时候,首先会去常量池定位一个类的符号引用,并检查这个类是否已经被加载,解析和初始化过。若是没有则进行类加载过程
分配内存:在类加载以后,就知道对象所须要的内存大小,接下来开始为对象分配内存。对象分配内存是在堆上完成的,划出一块未使用的内存给对象,分配的方式有两种:"指针碰撞","空闲列表"。到底采用哪一种分配方式取决于Java堆是否规整,Java堆是否规整又取决于垃圾收集器是否带有空间压缩整理(Compact)能力。
分配内存中为了解决线程安全问题有两种方案:1、对分配内存空间的动做进行同步处理,即虚拟机采用CAS配上失败重试的方式保证更新操做的原子性。2、使用本地线程分配缓冲区(TLAB),哪一个线程要分配内存,就在哪一个线程的本地缓冲区进行分配,只有缓冲区用完了,在分配新的缓冲区的时候才须要同步锁定。
赋初始值:保证对象的实例字段不赋初始值就能够直接使用,能够直接访问这些字段的初始值。
虚拟机对对象设置:虚拟机会将一些必要信息保存在对象头中,如:这个对象是哪一个类的实例,如何才能找到类的元数据信息,对象的哈希码等等。
执行构造函数:此时站在虚拟机角度看对象已经建立好了,可是此时对象中字段仍是默认零值,须要执行构造函数,按照设计意图构造好。