标签:模型 锁定 定位 本机 问题 布局 asp 本质 err
每天按照书本学一点,会把自己的总结思考写下来,形成输出,持续更新,立帖为证
-- 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)能力。
分配内存中为了解决线程安全问题有两种方案:一、对分配内存空间的动作进行同步处理,即虚拟机采用CAS配上失败重试的方式保证更新操作的原子性。二、使用本地线程分配缓冲区(TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区进行分配,只有缓冲区用完了,在分配新的缓冲区的时候才需要同步锁定。
赋初始值:保证对象的实例字段不赋初始值就可以直接使用,可以直接访问这些字段的初始值。
虚拟机对对象设置:虚拟机会将一些必要信息保存在对象头中,如:这个对象是哪个类的实例,如何才能找到类的元数据信息,对象的哈希码等等。
执行构造函数:此时站在虚拟机角度看对象已经创建好了,但是此时对象中字段还是默认零值,需要执行构造函数,按照设计意图构造好。
标签:模型 锁定 定位 本机 问题 布局 asp 本质 err
原文地址:https://www.cnblogs.com/leduoi/p/13308319.html