目录
前言
1.java内存区域简介
1.1 方法区 (Method Area)
1.2 堆区 (Heap)
1.3 程序计数器 (pc 寄存器):
1.4 Java 虚拟机栈(JVM Stacks)
1.5 本地方法栈(Native Mthod Stacks)
1.6 运行时常量池
2. 元数据以及java创建对象过程
2.1.元数据
2.2 具体实例
2.3 java创建对象的过程
3.垃圾回收机制
3.1.GC简介
3.1.1 不同的垃圾回收器的作用位置
3.1.2.垃圾回收器执行时机:
3.2垃圾判断算法
3.2.1 引用计数法
3.2.2 可达性分析算法
3.2.3 引用判断
3.3 垃圾回收算法
3.3.1 标记-清除算法
3.3.2 标记-整理算法
3.3 .3 复制算法
3.3 .4 分代收集算法
前言
我们在使用计算机的过程中,难免会产生许多使用过废弃的一些文件,正常情况下,我们会使用各种清理软件对垃圾进行清理。而在java编程中,我们同样会产生许多创建使用后丢弃的对象,那么,在java虚拟机中的这些对象我们要如何删除,该如何回收了?这就需要了解java虚拟机内存的分布情况以及相应的GC机制。
1.java内存区域简介
java虚拟机在执行java程序时会将他管理的内存划分为若干个不同的数据区域。
1.1 方法区 (Method Area)
方法区是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与 Java 堆区分开来。**
HotSpot 虚拟机中方法区也常被称为 **“永久代”**,本质上两者并不等价。仅仅是因为 HotSpot 虚拟机设计团队用永久代来实现方法区而已,这样 HotSpot 虚拟机的垃圾收集器就可以像管理 Java 堆一样管理这部分内存了。但是这并不是一个好主意,因为这样更容易遇到内存溢出问题。
相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。
1.2 堆区 (Heap)
Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建
1.3 程序计数器 (pc 寄存器):
程序计数器也称为(pc寄存器)是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。
1,通过线程拿到cpu的执行权,执行程序,当执行完毕以后,释放cpu的执行权
2.保存线程的位置,当前线程执行完毕后,程序计数器根据线程的泣置获取该线程,然后执行
注意:程序计数器是唯一一个不会出现QutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
1.4 Java 虚拟机栈(JVM Stacks)
与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
Java 堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代:在细致一点有:Eden空间、From Survivor、To Survivor空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
1.5 本地方法栈(Native Mthod Stacks)
和虚拟机栈所发挥的作用非常相似,区别是: **虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。
方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 两种异常。
1.6 运行时常量池
运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池信息(用于存放编译期生成的各种字面量和符号引用)
既然运行时常量池时方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。
2. 元数据以及java创建对象过程
2.1.元数据
概念——关于数据的数据或者叫做用来描述数据的数据或者叫做信息的信息。
我们可以把元数据简单的理解成,最小的数据单位。元数据可以为数据说明其元素或属性(名称、大小、数据类型、等),或其结构(长度、字段、数据列),或其相关数据(位于何处、如何联系、拥有者)。
特点
1.元数据是关于数据的结构化的数据,它不一定是数字形式的,可来自不同的资源。
2.元数据是与对象相关的数据,此数据使其潜在的用户不必先具备对这些对象的存在和特征的完整认识。
3.元数据是对信息包裹(Information Package)的编码的描述。
4.元数据包含用于描述信息对象的内容和位置的数据元素集,促进了网络环境中信息对象的发现和检索。
5.元数据不仅对信息对象进行描述,还能够描述资源的使用环境、管理、加工、保存和使用等方面的情况。
6.在信息对象或系统的生命周期中自然增加元数据。
2.2 具体实例
java代码操作数据库时,哪些是元数据信息?
1.ResultSet 结果集(保护:二维表结构(字段,类型,所在列的编号等),数据)
2. ResultSetmetaData元数据;二维表结构(字段,类型,所在列编号等)
3.持久层框架的问题
简单操作:将一行数据分封装到javaBean 简单操作:将一行数据分封装到javaBean
User user=mapper.selectPrimaryKey(1); user.uid,username,password tab_user.uid,username,password
2.3 java创建对象的过程
检测类是否被加载没有加载的先加载→为新生对象分配内存→将分配到的内存空间都初始化为零值→对对象进行必要的设置→执行
1. 检测类是否被加载
虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。
2.为新生对象分配内存
在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定。
3.初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
4.进行必要的设置
接下来,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头之中。
5、执行init方法
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从Java程序的视角来看,对象创建才刚开始,
3.垃圾回收机制
3.1.GC简介
在真实工作中的项目中,时不时的会发生内存溢出、内存泄露的问题,这也是不可避免的Bug,这些潜在的Bug在某些时候会影响到项目的正常运行,如果你的项目没有合理的进行业务内存分配,将会直接影响到的项目的并发处理,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。而了解了GC实现机制则是我们一切监控和调节的前提。
GC机制,通俗来讲,就是JVM虚拟机在清理java中一些已经死亡的对象的一个回收机制。而要详细了解回收机制,首先要了解java虚拟机中堆内存的结构。在Java虚拟机中进行垃圾回收的场所有两个,一个是堆,一个是方法区。在堆中存储了Java程序运行时的所有对象信息,而垃圾回收其实就是对那些“死亡的”对象进行其所侵占的内存的释放,让后续对象再能分配到内存,从而完成程序运行的需要。
3.1.1 不同的垃圾回收器的作用位置
Minor GC:新生代
Major Gc:老年代
Full GC:整个堆(通常用在老年代,永久代)
3.1.2.垃圾回收器执行时机:
Minor GC:当新生代空间不足时,会执行Minor Gc
Major GC: 当老生代空间(新生代空间不足)不足时,会执行Minor GcFull GC:当永久代(或老年代)空间不足时,会执行GC
1.当Full GC执行时,清理老年代空间时,执行完后,空间依然不足
outofmemoryError: java heap space
2.当Full GC执行时,清理永久代空间时,执行完后,空间依然不足
3.2垃圾判断算法
在 JVM 的眼中,垃圾就是指那些在堆中存在的,已经“死亡”的对象。而对于“死亡”的定义,我们可以简单的将其理解为“不可能再被任何途径使用的对象”。那怎样才能确定一个对象是存活还是死亡呢?这就涉及到了垃圾判断算法,其主要包括引用计数法和可达性分析法。
3.2.1 引用计数法
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加1;当引用失效,计数器就减1;任何时候计数器为0的对象就是不可能再被使用的。反之,当引用失效时,例如一个对象的某个引用超过了生命周期(出作用域后)或者被设置为一个新值时,则之前被引用的对象的计数器的值就减 1。而那些引用计数为 0 的对象,就可以称之为垃圾,可以被收集。
- 优点:引用计数法实现起来比较简单,对程序不被长时间打断的实时环境比较有利。
- 缺点:需要额外的空间来存储计数器,难以检测出对象之间的循环引用。
3.2.2 可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
3.2.3 引用判断
无论是引用计数算法,还是可达性分析算法,对象是否存货都跟 “引用” (reference)有关,在JDK1.2 之后,引用可分为以下4个
强引用:直接 new ,如 new Object(); 只要这类强引用还在,对象就不会回收
软引用:(SoftReference类)用来描述一些还有用但非必需的对象;当将来发生内存溢出之前,系统会把这些有软引用的对象列入回收范围中进行二次回收,如果这次回收还没有足够的内存,则内存溢出报异常;
弱引用:(WakeReference类) 被弱引用的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器开始工作时,无论内存是否足够,都会对这些对象进行回收。
虚引用(幽灵引用/幻影引用):(PhantomReference类) 一个虚引用的对象,它的唯一目的就是能在这个对象被收集器回收时收到一个系统通知,即一个对象是否有虚引用,完全不会对其生存时间构成影响。
3.3 垃圾回收算法
3.3.1 标记-清除算法
标记-清除(Tracing Collector)算法是最基础的收集算法,为了解决引用计数法的问题而提出。它使用了根集的概念,它分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实就是前面的可达性分析法中判定垃圾对象的标记过程。
3.3.2 标记-整理算法
标记-整理(Compacting Collector)算法标记的过程与“标记-清除”算法中的标记过程一样,但对标记后出的垃圾对象的处理情况有所不同,它不是直接对可回收对象进行清理,而是让所有的对象都向一端移动,然后直接清理掉端边界以外的内存。在基于“标记-整理”算法的收集器的实现中,一般增加句柄和句柄表。
3.3 .3 复制算法
复制(Copying Collector)算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它将内存按容量分为大小相等的两块,每次只使用其中的一块(对象面),当这一块的内存用完了,就将还存活着的对象复制到另外一块内存上面(空闲面),然后再把已使用过的内存空间一次清理掉。
3.3 .4 分代收集算法
分代收集(Generational Collector)算法的将堆内存划分为新生代、老年代和永久代。新生代又被进一步划分为 Eden 和 Survivor 区,其中 Survivor 由 FromSpace(Survivor0)和 ToSpace(Survivor1)组成。所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。分代收集,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,可以将不同生命周期的对象分代,不同的代采取不同的回收算法进行垃圾回收,以便提高回收效率。



