java 内存管理

释放双眼,带上耳机,听听看~!

引用类型

  • 强引用:Person p=new Person();一般使用的都是强引用

  • jvm不会回收强引用

    • 如果内存不足则oom
    • 如果想切断强引用:Person p=null
  • 软引用:SoftReference

  • 在内存不足时,抛出oom前会回收软引用.通常被用来缓存一些重要但是非必须的数据


1
2
3
4
5
6
7
8
1        软引用:如果内存充足,是不会回收的。如果内存不足会将较早的对象回收
2        SoftReference[] t2=new SoftReference[count];
3        for (int i=0;i<count;i++){
4            t2[i]=new SoftReference(new TestObj("ming"+i));
5        }
6        System.out.println(t2[9000].get());//能正常获取
7        System.out.println(t2[0].get());//内存不足被回收了
8
  • 弱引用: WeakReference

  • 发现弱引用就会被回收(执行System.gc())


1
2
3
4
5
6
7
8
9
10
11
12
1        弱引用:存在到下一次垃圾回收之前,无论内存是否充足都会被回收
2        int weakReferenceLength=100;
3        WeakReference[] t3=new WeakReference[weakReferenceLength];
4        for (int i=0;i<weakReferenceLength;i++){
5            t3[i]=new WeakReference(new TestObj("ming"+i),queue);
6            if (i==1){
7//                System.gc();
8                System.out.println(t3[1].get());//能正常获取
9            }
10        }
11        System.out.println(t3[1].get());//内存充足,但可能已经被回收了
12
  • 虚引用:用来探测对象有没有被回收

  • 任何时刻都有可能被回收

四种引用的概念:https://www.daimajiaoliu.com/daima/4edecb535900407

不同引用存在的意义

  • 对gc回收时机不可控的一个妥协,使得我们控制回收的时间点
  • eg:用来当做缓存存放经常访问的数据,用来作为缓存存放一些不太重要的静态资源

对象存活判定

引用计数法

  • 有引用时加一,没有引用的时候减一,为零时回收
  • 问题:这种算法很难解决对象之间相互引用的情况

可达性分析算法

  • “GC Roots”的对象作为起始点,当对象没有办法到达gc root对象,则认为不可达

  • gc root对象:

  • 虚拟机栈中的引用对象

    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中引用的对象

方法区的垃圾回收

  • 主要回收废弃的常量、无用的类
  • 常量:如果程序中没有引用在指向该常量,则可以被回收。例如常量池中的”abc”,如果没有String对象指向“abc”则进行回收
  • 无用类:1、该类的所有实例都已经回收 2、加载该类的classLoader已经回收 3、该类对应的class对象不在使用

垃圾收集算法

标记清除

  • 首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象
  • 不足:标记和清除的效率都不高;清除后产生大量的碎片空间,如果分配较大的对象可能空间不够再次触发gc

复制算法

  • 每次只使用一半的空间,gc时将存活对象复制到另一般内存中,再对当前的内存进行清除
  • 不足:浪费内存,有一半的内存没有利用。不过这种算法的改进版被用在回收新生代中,新生代中的对象每次回收都基本上只有10%左右的对象存活,所以需要复制的对象很少,效率很高

标记整理

  • 让所有存活对象都向内存一端移动,然后直接清理掉边界以外的内存
  • 这种算法主要用在老年代这种不易被回收的对象上。这类对象存活率高,如果直接复制效率很低

分代收集算法

  • 现在jvm主要使用的gc算法。主要就是结合复制算法(针对新生代:大批的对象死去,存活率不高)和标记整理算法(针对老年代:对象相对稳定,不需要额外的空间担保)

垃圾收集器

  • 就是上面收集算法的具体实现,在HotSpot中如下:

java 内存管理

Serial收集器

  • 采用复制算法,单线程收集。收集过程中会阻塞其他的线程(可有停顿)。可以获取最高的单核效率
  • Serial收集器依然是虚拟机运行在Client模式下的默认新生代收集器

ParNew收集器

  • ParNew收集器其实就是Serial收集器的多线程版本,除了多线程外没什么特别
  • 但是它却是Server模式下的虚拟机首选的新生代收集器

java 内存管理

Parallel Scavenge收集器

  • 利用复制算法,多线程的收集内存
  • 但是关注点在吞吐量(吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间))。适合于后台大运算量,没有太多的用户交互

Serial Old收集器

  • 单线程,标记整理算法。虚拟机运行在Client模式下的默认老生代收集器

Parallel Old收集器

  • Parallel Scavenge收集器的老年代版本,使用标记整理算法
  • 注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge收集器+Parallel Old收集器的组合

CMS

  • CMS(Conrrurent Mark Sweep)收集器是以获取最短回收停顿时间为目标的收集器

G1

  • 将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分(可以不连续)Region的集合。
  • 未来可能替换掉cms

https://www.daimajiaoliu.com/daima/34012c09a900405

新生代、老年代、永久代

  • 新生代发生的GC也叫做MinorGC,MinorGC发生频率比较高,不一定等 Eden区满了才触发
  • 老年代存放的都是一些生命周期较长的对象,在新生代中经历了N次垃圾回收后仍然存活的对象就会被放到老年代中。老年代满后会触发:Major GC(Full GC),对新生代、老生代进行回收。触发条件:老年代被写满、永久代(Perm)被写满和System.gc()被显式调用等。
  • 永久代主要用于存放静态文件,如Java类、方法等。如果需要动态生成class,需要较大的永久代

https://www.daimajiaoliu.com/daima/4760f33dd100401

内存划分如下

java 内存管理

当前线程的内存

程序计数器

  • 通过改变这个计数器的值来选取下一条需要执行的字节码指令。
  • 在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一条线程中的指令,确保在线程切换后能正常恢复

虚拟机栈

  • 用于存储局部变量表、操作栈、动态链接、方法出口等

  • 局部变量表需要的内存一旦分配好不能改变

  • 一般程序员关注的堆栈:“堆”为java中个线程共享的堆内存,”栈”就是现在讲的虚拟机栈,或者说是虚拟机栈中的局部变量表,存放了编译期可知的各种基本数据类型或者对象引用

  • 其中64位长度的long和double类型的数据会占用2个局部变量空间(Slot)

  • 异常情况:

  • 栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常

    • 当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常

本地方法栈

  • 本地方法栈则是为虚拟机使用到的Native方法服务
  • 有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常

所有线程共享的内存

  • Java堆是被所有线程共享的一块内存区域
  • 唯一目的就是存放对象实例和数组
  • Java堆是垃圾收集器管理的主要区域
  • 现在收集器基本都是采用的分代收集算法
  • Java堆中还可以细分为:新生代和老年代
  • 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常

方法区(Method Area)

  • 各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 对于HotSpot虚拟机,很多人愿意把方法区称为”永久代”Permanent Generation),本质上两者并不等价,仅仅是因为HotSpot虚拟机的设计团队选择把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。
  • 垃圾收集行为在这个区域是比较少出现的
  • 这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载
  • 当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常

运行时常量池(Runtime Constant Pool)是方法区的一部分

  • 编译期生成的各种字面量和符号引用,类加载后存放到方法区的运行时常量池中
  • 运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法

给TA打赏
共{{data.count}}人
人已打赏
安全技术

网站制作需要素材的实用网站

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索