Java堆是jvm内存中被所有线程共享的一块区域,该区域物理上可以不连续但是逻辑上连续即可。Java堆存放着程序运行中生成的对象实例数据。在jdk1.7及以上版本,Hotspot虚拟机将运行时常量池和静态变量从方法区中移到了Java堆中。每个对象在Java堆上分配内存时,并发场景下,虚拟机采用CAS自旋锁和TLAB的方式保证内存的安全分配。具体分配方式根据不同垃圾收集器大体上分为指针碰撞和空闲列表两种。基于对象垃圾回收的强弱分代假说,即大多数对象是朝生夕死的,熬过越多次垃圾回收的对象越不容易消亡;主流虚拟机将Java堆划分为新生代和老年代,默认比例1:2,可以通过jvm参数修改。其中新生代中又具体划分为一个Eden区和两个Survivor区,默认比例8:1:1,可以通过jvm参数修改。堆内存的大小也可以通过jvm参数指定。垃圾收集器主要就是收集堆中的数据,经过垃圾回收后仍然无法给新对象分配内存时,此区域会触发OOM错误。
成都创新互联公司服务项目包括巴彦淖尔网站建设、巴彦淖尔网站制作、巴彦淖尔网页制作以及巴彦淖尔网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,巴彦淖尔网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到巴彦淖尔省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!2.虚拟机栈虚拟机栈是jvm内存中线程私有的一块区域,它与线程的生命周期相同。虚拟机栈的存储单位是栈帧,每个方法的执行和结束代表着一个栈帧的入栈和出栈。每个栈帧中存储着局部变量表、操作数栈、动态链接、方法返回地址和一些附加信息。栈内存大小决定了方法调用的深度,方法调用深度过大,如递归调用时,容易引发StackOverflow错误,一个线程的创建就会伴随着创建一个虚拟机栈,而创建线程也会占用jvm内存,当jvm无法为新线程分配内存时,此区域也会引发OOM错误。虚拟机栈的大小可以通过jvm参数指定。
3.本地方法栈本地方法栈与虚拟机栈类似,也是属于线程私有的一块区域。只不过本地方法栈是jvm执行的native方法,native方法是jdk环境提供的本地方法,由c或c++语言实现。此区域同样会有StackOverflow和OOM的风险。
4.方法区方法区是jvm虚拟机的设计规范,其具体实现为永久代和元空间,此区域是所有线程共享的一块区域。在jdk1.6及以前,Hotspot虚拟机采用永久代的方式实现方法区。可以通过jvm参数设置永久代的大小。永久代中主要存放着类的Class字节码信息、静态变量、运行时常量池和JIT即时编译器编译后的代码,由于这些数据在程序运行中被回收的概率很小,但是却占据着宝贵的jvm内存,在一些热部署的场景下,加载大量类信息,很容易引发此区域OOM,然而这些风险在系统的正常运行中不易发现。所以从jdk1.7开始,考虑将永久代从jvm内存中移除,直接使用操作系统内存存储这些信息。jdk1.7时,已经将运行时常量池、静态变量从永久代中转移到堆中存储。到jdk1.8时,Hotspot虚拟机彻底将永久代从jvm内存中移除,转而使用MetaSpace代替永久代,metaSpace使用的是操作系统和的内存。因此不会再出现OOM的错误。(除非超过了操作系统的内存)。
5.运行时常量池运行时常量池是程序运行中储存Class对象的常量信息,包括字面量和符号引用。一个Class文件对应一个Class常量池,在程序运行时,Class文件被加载到jvm内存中时,就会产生一个Class运行时常量池。在jdk1.6及以前,每个Class的运行时常量池中还存在一份字符串常量池,多个Class运行时常量池中就会存在多份字符串常量池,而这些字符串常量池中的常量有很多是重复的,所以浪费了很大的内存空间,于是jdk从1.7开始,将字符串常量池从每个Class运行时常量池中移出来,统一成一份存储在堆空间中。避免了内存空间的浪费。
6.直接内存直接内存并不是Java虚拟机规范中定义的内存区域。jdk1.4时引入了NIO网络传输模型,它可以通过native函数库直接分配堆外内存,因为避免了Java堆和native堆来回复制数据的操作,在频繁进行IO操作的场景下性能显著提高,直接内存与Java堆内存相比,因为无法利用Java堆分配内存的优良特性,并发场景下申请直接内存更耗费性能,但是直接内存在频繁IO操作的场景下性能要由于Java堆内存。
7.为什么堆内存要分年轻代和老年代?堆内存好比是一块收纳空间,如果没有合理的规划空间的划分,很容易造成频繁使用的东西难以找到,经常不用的东西随处可见,而且东西乱丢乱放也很容易造成空间资源的浪费。基于垃圾回收的强弱分代假说,大多数对象都是朝生夕死的,坚持越久不被回收的对象越不容易消亡,所以将堆内存划分为年轻代和老年代,目的就是将朝生夕死的对象和持续不死的对象划分开,从而提升垃圾回收的效率。
8.一个对象的创建过程jvm识别到new指令时,会先检查指令的参数是否在运行时常量池中能定位到这个类的符号引用,并检查符号引用代表的类是否已经被加载过,解析和初始化过,如果没有,则会先执行相应的类加载过程,如果有,则会开始为该对象在堆空间分配内存。jvm将分配到的内存都初始化为零值,紧接着为对象设置对象头信息,包括类元数据信息、哈希值、GC分代年龄等。接下来会执行init方法,按照开发者意愿给对象中的变量赋值。至此,对象创建完毕。
9.一个对象的内存分配对象在堆空间上分配内存,不同的垃圾收集器采用的方式有所不同,主要分为指针碰撞和空闲列表两种分配方式。如果堆内存是规整的,所有已使用的内存放到一边,空闲的放到另一边,中间维护一个指针作为分界点,当为新对象分配内存时,只需要把指针向空闲方向移动一段与新对象大小相等的实例,这种分配方式称为指针碰撞。如果堆内存是不规整的,jvm必须维护一个列表,记录哪些内存可以使用,在分配内存时,需要在列表中找到一块空间分配给对象,然后更新列表上的记录,这种分配方式称为空闲列表。对象在堆中分配内存时及其频繁的操作,为了解决线程安全的问题,jvm采取TLAB的方式,每个线程在堆中预先分配一小块内存,若TLAB分配内存失败,jvm会采取CAS自旋锁的方式直接在堆中分配内存,保障内存分配时的线程安全。
新对象大多数会在新生代的Eden区分配到内存,当新对象很大,Eden分配失败时,YoungGC触发前,首先会进行老年代空间担保判断,若老年代的可用内存小于新生代所有存活对象占用的内存大小,会再次判断老年代的可用内存是否小于之前YoungGC后新生代对象进入老年代的平均大小,如果小于,直接触发FullGC,FullGC后若新对象能够成功分配内存,则直接在老年代中分配内存,若分配失败,报OOM错误;如果大于,首先会触发一次YoungGC,YoungGC后,新生代中存活的对象小于老年代可用内存的前提下,会将新生代中的存活对象年龄默认超过15直接进入到老年代;Survivor区中的存活对象大小超过了该区域的50%时,会将大于等于这批存活对象年龄大值的所有对象直接进入到老年代;若新生代中存活对象大小超过Survivor区,也会直接进入到老年代。如果YoungGC后,新生代中存活的对象大小超过老年代可用内存,会再次触发FullGC,FullGC后继续将新生代存活对象进入到老年代,失败报OOM,新生代对象进入老年代成功后,在老年代为新对象直接分配内存,失败报OOM。
10.一个对象的销毁过程待补充
11.对象的两种访问方式待补充
12.为什么需要内存担保?待补充
13.垃圾收集算法有哪些?垃圾收集器有哪些?他们的特点是什么?待补充
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧