手记

JVM内存分配和垃圾收集策略

java内存区域

程序计数器

  因为java可以多线程并发执行,因此,为了线程切换后能恢复到正确的执行位置,每个线程都需要一个独立的程序计数器。记录正在执行的虚拟机字节码指令的地址。

  这个区域不会产生内存溢出异常。

  java虚拟机栈

    栈中主要存放了编译期可知的四类八种基本数据类型存(逻辑型 boolean、文本型char、整数型byte、short、int、float、浮点数型double、long),对象引用类型,和对象引用类型(reference)。

  本地方法栈

    本地方法栈和java虚拟机栈所发挥的作用非常相似,他们之前的区别是虚拟机栈为虚拟机执行java方法服务。而本地方法栈是为虚拟机使用到的Native方法服务。

    -Xss参数可以设置本地方法栈的内存上限。

    如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常。

    程序中 递归如果找不到出口也会抛出此异常(不断的进行入栈操作)

  用于存放对象的实列

  可通过-Xms和Xmx来扩展堆的大小。因为现在的收集器基本都采用分代收集算法,所以java堆中还可以细分:新生代和老年代

  如果在堆中没有内存完成实列分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。-Xmx可以设置堆内存的上限 -Xms 可以设置内存初始化的大小

方法区(永久代、也叫非堆)

  用于储存已被虚拟机加载的类的信息(编译后的class)、常量、静态变量、即时编译器编译后的代码等数据,Class在被Loader时就会被放到方法区(PermGen space)中

  垃圾收集行为在这个区比较少出现的,有点类似它的名字,永久代

  当方法区无法满足内存分配需求时,将抛出OutOfMemoryErro(后面会跟PermGen space字符串)异常。-XX:MaxPermSize可以设置方法区的上限。

运行时常量池(方法区的一部分)

  用于存放编译期生成的各种字面量和符号的引用,既然运行时常量池是方法区的一部分,自然收到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

  JAVA的String声明的变量就是放在运行时常量池中比如String str="test"; 。但是 String str=new String("test");是new一个对象 是放在堆中

  每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。

  如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串


 

以上

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

如果虚拟机在扩展时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。可通过-Xmx和-XX:MaxPermSize可以设置内存和方法区上限。-Xmx2048m  -XX:MaxPermSize=512m

把-Xmx 和-Xms大小设置一样内存大小,可以提高JVM的运行性能(取消掉伸缩区)

回到顶部

JVM垃圾收集算法

标记——清除算法

  首先标记出所有需要回收的对象,在标记完成后统一回收。

  缺点:这种回收算法会产生大量不连续的内存碎片。以后程序运行过程中产生较大的对象时,无法找到足够连续的内存 而不得不提前触发另一次垃圾收集动作。

复制算法

  将内存按容量划分大小相等的两块,每次只使用一块。当这一块的内存用完了,就将还存活的对象复制到另外一块内存上面。

  缺点:将内存的使用率缩小了一半

标记——整理算法

  首先标记出所有需要回收的对象,然后让所有存货的对象都向一端移动。然后直接清理掉端边界意外的内存

分代收集算法

  我们jdk采用的应该就是分代收集算法。根据对象存货周期的不同将内存划分为几块,针对每一块使用不同的算法

  新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要对少量存活的对象复制就可以进行收集。

  老年代中,因为对象存货率高,没有额外空间对它进行分配担保,就需要使用 标记——整理或者标记——清楚算法。

回到顶部

基于JDK命令行工具的监控

更新中。。。。。。

原文出处:JVM内存分配和垃圾收集策略


0人推荐
随时随地看视频
慕课网APP