Java基础之JVM

一、Java中的内存管理
1、程序,无论是代码还是数据,都需要存储在内存中。JVM为Java程序提供并管理所需要的内存空间
2、JVM内存分为堆(heap)、栈(stock)、方法区(method)三个区域,分别用于储存不同的数据。
3、HotSpot是Sun JDK和Open JDK中所带的虚拟机(Sun JDK和Open JDK除了注释,代码实现基本上是相同的)。
 
二、堆
1、 JVM只有一个堆区,在虚拟机启动时创建,被所有线程共享,堆区不放基本类型(成员变量除外)和对象的引用,只存储对象本身(包括class对象和异常对象)和数组,堆是GC所管理的主要区域(对不需要的对象进行标记,而后进行清除)。
2、堆内存的划分

 

3、GC流程
  • 基本所有数据都会保存在JVM的堆内存之中。
  • 对于整个的GC流程里面,最需要处理的事是 年轻代 和 老年代 的内存清理操作。
  • 元空间(永久代)都不在GC范围内。
 

 

新生代的GC的时候使用复制算法
老年区的GC的时候使用标记-清除算法
 
垃圾回收的类型
串行GC
并行GC
为并发GC
 
GC具体流程:
  1. 当现在有一个新生的对象产生,JVM需要为该对象进行内存空间申请。
  2. 先判断Eden区是否有内存空间,如果有,直接将新对象保存在Eden区。
  3. 如果Eden区的内存空间不足,会自动执行一个Minor GC操作,将Eden区无用内存空间进行清理。
  4. 清理Eden区之后继续判断Eden区内存空间情况,如果充足,则将新对象直接保存在Eden区。
  5. 如果执行了Minor GC之后发现Eden区的内存仍然不足,那就判断存活区的内存空间,并将Eden区的部分活跃对象保存在存活区。
  6. 活跃对象迁移到存活区后,继续判断Eden区内存空间情况,如果充足,则将新对象直接保存在Eben区。
  7. 如果存活区也没有空间了,则继续判断老年区,如果老年区充足,则将存活区部分活跃对象保存在老年区。
  8. 存活区的活跃对象迁移到老年区后,则将Eden区的部分活跃对象保存到存活区。
  9. 活跃对象迁移到存活区后,继续判断Eden区内存空间情况,如果充足,则将新对象直接保存在Eden区。
  10. 如果老年区也满了,这时候产生Major GC(full GC)进行老年区的内存清理。
  11. 如果老年区执行Major GC之后发现无法进行对象保存,会产生OutOfMemoryError异常。
 
堆内存参数调整:(调优关键)

 

三、栈
1、每个线程包含一个栈区(堆只有一个所有线程共享),栈中只保存基本数据类型的对象和自定义对象的引用,对象都存放在堆区中。
2、每个栈中的数据(原始类型 和 对象引用)都是私有的,其他栈不能访问。
3、栈分为3部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
4、过程:栈用于存储程序运行时在方法中声明的所有局部变量(栈主要存储方法中的局部变量)。JVM会为每一个方法分配一个对应的空间,这个空间称为该方法的栈帧。一个栈帧对应一个正在调用的方法,栈帧中存储了该方法的参数、局部变量等数据。当某一方法调用完成后,其对应的栈帧将被清除,局部变量失效。(方法结束,局部变量失效,从栈中清除)
 
四、方法区
1、方法区又叫静态区,里存储着class文件的信息和动态常量池,class文件的信息包括类信息和静态常量池。
2、用于储存已被虚拟机加载的类信息、常量、静态常量、即使编译器编译后的代码等数据。
3、垃圾收集行为在方法区很少出现,这块区域回收的主要目标是针对常量池的回收和对类型的卸载。
4、运行常量池是方法区的一部分,常量池用于存放编译期生成的各种字面量和符号引用(还有翻译出来的直接引用),这部分内容在类加载后进入方法区的运行时常量池中存放。
5、运行时常量池相对于Class文件常量池的另一个重要特征是具备动态性,运行期也可能将新的常量放入池中。
6、字面量:如文本字符串,声明为final的常量值等。
7、符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。在对java文件进行编译的过程中,并不会向C语言那样有连接这一步,也就是说class文件中不会存储方法、字段的最终内存布局信息,所以符号引用是不能被虚拟机直接使用的,虚拟机会在加载类时动态的去获取常量池中的符号引用,然后解析到对应的内存地址中,才可以使用
8、方法区用于存放类的信息,Java程序运行时,首先会通过类装载器载入文件的字节码信息,经过解析后将其装入方法区。类的各种信息(包括方法)都在方法区储存。(将类的成员都加载到方法区)类在实例化对象时,多个对象会拥有各自在堆中的空间,但所有实例对象是共用在方法区中的一份方法定义的。方法只有一份。
 
 
五、Java堆栈方法区总结
1、基础类型直接在栈空间分配,方法的形式参数直接在栈中分配,当方法调用完成后从栈空间回收。
2、引用数据类型,需要new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量。
3、方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,在方法调用完成后从栈空间回收。
(引用方法非常多的时候会怎样?)
4、局部变量new出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间会被立即回收,堆空间区域等待GC回收。
5、方法调用时传入literal(常量)参数,在方法调用完成后从栈空间分配。
6、字符串常量在 DATA 区域分配 ,this 在堆空间分配 。
7、数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小。
8、static在DATA区域分配。
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈是操作系统在建立某个进程或者线程(在支持多线程的操作系统中是线程)上为这个线程建立的储存区域,该区域具有先进后出的特性。
每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中创建的所有类实例或数组都放在堆中,并由应用所有的线程共享,跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的储存空间都是在堆中分配的,但是这个对象的引用却是在栈中分配的,也就是说建立一个对象时从两个地方都分配了内存,在堆中分配的内存实际上建立了这个对象,而在栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。
 
 
 
 
posted @ 2024-05-31 14:43  weigang  阅读(15)  评论(0编辑  收藏  举报