以下为博主整理网络资料而成,如有错误望请指正,感谢!
JVM
JVM运行时数据区域
根据JVM规范,JVM内存共分为虚拟机栈,堆,方法区,程序计数器,本地方法栈五个部分
1. 虚拟机栈
每个线程有一个私有的栈,随线程的创建而创建。
该栈中一种叫“栈帧”的东西,每一个方法会创建一个“栈帧”,
用于存放局部变量表(基本数据类型和对象引用)、操作数栈、方法出口等信息
2. 本地方法栈
该部分与虚拟机用到的Native方法相关,一般情况下,Java程序员不需要关系这部分内容。
3. PC寄存器(程序计数器)
JVM支持多个线程同时运行,每个线程都有自己的程序计数器。
若当前执行的是JVM的方法,则PC寄存器保存当前执行指令的地址;
若是本地方法(native方法),则PC寄存器为空。
4. 堆(GC堆)
堆是JVM所有线程共享的部分,在虚拟机启动的时候就已经创建。
所有对象和数组在堆中进行分配。
Java堆不需要连续内存,并且可以通过动态增加其内存。
细分:新生代和老年代;
新生代又分Eden空间、From Survivor空间、To Survivor空间。
5. 方法区
方法区也是所有线程共享。主要用于存储类的信息、常量池、方法数据、方法代码等。
方法区逻辑上属于堆的一部分,但为与堆进行区分,通常又叫“非堆”。
JVM内存
-
Java把内存分两种:栈内存和堆内存。简单来讲,堆内存用于存放有new创建的对象和数组,在堆中分配的内存,有java虚拟机自动垃圾回收器来管理。而栈内存由使用的人向系统申请,申请人进行管理。
-
JVM堆内存
-
JVM堆内存分两个区域:年轻代(YoungGen)、老年代(OldGen)
- 年轻代两部分:Eden(生成区)、Survivor(幸存区)
- Survivor两部分:FromSpace、ToSpace
- Eden区占大容量,Eden:From Survivor:To Survivor默认比例8:1:1
-
常用参数
-Xms 堆内存初始大小,单位M、G
-Xmx(MaxHeapSize) 堆内存最大允许大小,一般不要大于物理内存的80%
-XX:PermSize 非堆内存初始大小,一般应用设置初始化200m,最大1024m就够了
-XX:MaxPermSize 非堆内存最大允许大小
-XX:NewSize(-Xns) 年轻代内存初始大小
-XX:MaxNewSize(-Xmn) 年轻代内存最大允许大小,也可以缩写
-XX:SurvivorRatio=8 年轻代中Eden区与Survivor区的容量比例值,默认为8,即8:1
-Xss 堆栈内存大小
-
-
JVM非堆内存(方法区)
- jdk8之前,存在着永久代/持久代(PermGen)
- jdk8,永久代被元空间(meatspace)取代。但是元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
- 为什么用元空间取代永久代?
- 字符串存在永久代中,容易出现性能问题和内存溢出
- 为融合HotSpot JVM与JRockit JVM(新JVM技术)而做出的改变
-
内存申请过程:
- JVM会试图为相关Java对象在年轻代的Eden区中初始化一块内存区域。
- 当Eden区空间足够时,内存申请结束。否则执行下一步。
- JVM试图释放在Eden区中所有不活跃的对象(Young GC)。释放后若Eden空间仍然不足以放入新对象,JVM则试图将部分Eden区中活跃对象放入Survivor区。
- Survivor区被用来作为Eden区及年老代的中间交换区域。当年老代空间足够时,Survivor区中存活了一定次数的对象会被移到年老代。
- 当年老代空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC)。
- Full GC后,若Survivor区及年老代仍然无法存放从Eden区复制过来的对象,则会导致JVM无法在Eden区为新生成的对象申请内存,即出现“Out of Memory”。
Java中的四种引用类型级别
对象的引用四种级别由高到低:强引用、软引用、弱引用、虚引用
- 强引用
- 我们写代码使用的对象就是强引用,它有其他对象或方法、变量等等指向它的引用
- 如果一个对象被拥有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题
- 软引用
- 如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
- 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。
- 弱引用
- 如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
- 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。
- 如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象
- 虚引用
- "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。
- 虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动
java中堆(Heap)和栈(Stack)的区别
1. 栈内存用来存储局部变量和方法调用;
堆内存用来存储Java中的对象,无论是成员变量、局部变量、
还是类变量指向的对象,都存储在堆中。
2 栈内存归属于单个线程,每个线程都有一个栈内存(线程私有);
堆内存对所有线程可见,即堆内存中的对象可以被所有线程访问
3. 栈内存大小远远小于堆内存
4. 栈内存是连续的;堆内存在物理上是不连续的,在逻辑上是连续的
5. 栈是先进后出;堆是先进先出