java面试:JVM

以下为博主整理网络资料而成,如有错误望请指正,感谢!

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)取代。但是元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
    • 为什么用元空间取代永久代?
      1. 字符串存在永久代中,容易出现性能问题和内存溢出
      2. 为融合HotSpot JVM与JRockit JVM(新JVM技术)而做出的改变
  • 内存申请过程:

    1. JVM会试图为相关Java对象在年轻代的Eden区中初始化一块内存区域。
    2. 当Eden区空间足够时,内存申请结束。否则执行下一步。
    3. JVM试图释放在Eden区中所有不活跃的对象(Young GC)。释放后若Eden空间仍然不足以放入新对象,JVM则试图将部分Eden区中活跃对象放入Survivor区。
    4. Survivor区被用来作为Eden区及年老代的中间交换区域。当年老代空间足够时,Survivor区中存活了一定次数的对象会被移到年老代。
    5. 当年老代空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC)。
    6. Full GC后,若Survivor区及年老代仍然无法存放从Eden区复制过来的对象,则会导致JVM无法在Eden区为新生成的对象申请内存,即出现“Out of Memory”。

Java中的四种引用类型级别

对象的引用四种级别由高到低:强引用、软引用、弱引用、虚引用

  1. 强引用
    1. 我们写代码使用的对象就是强引用,它有其他对象或方法、变量等等指向它的引用
    2. 如果一个对象被拥有强引用,那么垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题
  2. 软引用
    1. 如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
    2. 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java 虚拟机就会把这个软引用加入到与之关联的引用队列中。
  3. 弱引用
    1. 如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
    2. 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。
    3. 如果一个对象只具有弱引用,那该类就是可有可无的对象,因为只要该对象被 gc 扫描到了随时都会把它干掉。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象
  4. 虚引用
    1. "虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用来跟踪对象被垃圾回收的活动。
    2. 虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

java中堆(Heap)和栈(Stack)的区别

1. 栈内存用来存储局部变量和方法调用;
	堆内存用来存储Java中的对象,无论是成员变量、局部变量、
	还是类变量指向的对象,都存储在堆中。
2 栈内存归属于单个线程,每个线程都有一个栈内存(线程私有);
	堆内存对所有线程可见,即堆内存中的对象可以被所有线程访问
3. 栈内存大小远远小于堆内存
4. 栈内存是连续的;堆内存在物理上是不连续的,在逻辑上是连续的
5. 栈是先进后出;堆是先进先出

推荐阅读博文1
推荐阅读博文2

你可能感兴趣的:(Java,java面试,jvm)