java 内存管理

引用类型

  • 强引用:Person p=new Person();一般使用的都是强引用
    • jvm不会回收强引用
    • 如果内存不足则oom
    • 如果想切断强引用:Person p=null
  • 软引用:SoftReference
    • 在内存不足时,抛出oom前会回收软引用.通常被用来缓存一些重要但是非必须的数据
        软引用:如果内存充足,是不会回收的。如果内存不足会将较早的对象回收
        SoftReference[] t2=new SoftReference[count];
        for (int i=0;i<count;i++){
            t2[i]=new SoftReference(new TestObj("ming"+i));
        }
        System.out.println(t2[9000].get());//能正常获取
        System.out.println(t2[0].get());//内存不足被回收了
  • 弱引用: WeakReference
    • 发现弱引用就会被回收(执行System.gc())
        弱引用:存在到下一次垃圾回收之前,无论内存是否充足都会被回收
        int weakReferenceLength=100;
        WeakReference[] t3=new WeakReference[weakReferenceLength];
        for (int i=0;i<weakReferenceLength;i++){
            t3[i]=new WeakReference(new TestObj("ming"+i),queue);
            if (i==1){
//                System.gc();
                System.out.println(t3[1].get());//能正常获取
            }
        }
        System.out.println(t3[1].get());//内存充足,但可能已经被回收了
  • 虚引用:用来探测对象有没有被回收
    • 任何时刻都有可能被回收

四种引用的概念:https://blog.csdn.net/cadi2011/article/details/50994496

不同引用存在的意义

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

对象存活判定

引用计数法

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

可达性分析算法

  • “GC Roots”的对象作为起始点,当对象没有办法到达gc root对象,则认为不可达
  • gc root对象:
    • 虚拟机栈中的引用对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中引用的对象

方法区的垃圾回收

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

垃圾收集算法

标记清除

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

复制算法

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

标记整理

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

分代收集算法

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

垃圾收集器

  • 就是上面收集算法的具体实现,在HotSpot中如下:
    这里写图片描述

Serial收集器

ParNew收集器

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

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.cnblogs.com/xiaoxi/p/6486852.html

新生代、老年代、永久代

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

https://blog.csdn.net/plgy_Y/article/details/72791483

内存划分如下

这里写图片描述

当前线程的内存

程序计数器

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

虚拟机栈

  • 用于存储局部变量表、操作栈、动态链接、方法出口等
    • 局部变量表需要的内存一旦分配好不能改变
  • 一般程序员关注的堆栈:“堆”为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()方法
技术选型 【后端】:Java 【框架】:springboot 【前端】:vue 【JDK版本】:JDK1.8 【服务器】:tomcat7+ 【数据库】:mysql 5.7+ 项目包含前后台完整源码。 项目都经过严格调试,确保可以运行! 具体项目介绍可查看博主文章或私聊获取 助力学习实践,提升编程技能,快来获取这份宝贵的资源吧! 在当今快速发展的信息技术领域,技术选型是决定一个项目成功与否的重要因素之一。基于以下的技术栈,我们为您带来了一份完善且经过实践验证的项目资源,让您在学习和提升编程技能的道路上事半功倍。以下是该项目的技术选型和其组件的详细介绍。 在后端技术方面,我们选择了Java作为编程语言。Java以其稳健性、跨平台性和丰富的库支持,在企业级应用中处于领导地位。项目采用了流行的Spring Boot框架,这个框架以简化Java企业级开发而闻名。Spring Boot提供了简洁的配置方式、内置的嵌入式服务器支持以及强大的生态系统,使开发者能够更高效地构建和部署应用。 前端技术方面,我们使用了Vue.js,这是一个用于构建用户界面的渐进式JavaScript框架。Vue以其易上手、灵活和性能出色而受到开发者的青睐,它的组件化开发思想也有助于提高代码的复用性和可维护性。 项目的编译和运行环境选择了JDK 1.8。尽管Java已经推出了更新的版本,但JDK 1.8依旧是一种成熟且稳定的选择,广泛应用于各类项目中,确保了兼容性和稳定性。 在服务器方面,本项目部署在Tomcat 7+之上。Tomcat是Apache软件基金会下的一个开源Servlet容器,也是应用最为广泛的Java Web服务器之一。其稳定性和可靠的性能表现为Java Web应用提供了坚实的支持。 数据库方面,我们采用了MySQL 5.7+。MySQL是一种高效、可靠且使用广泛的关系型数据库管理系统,5.7版本在性能和功能上都有显著的提升。 值得一提的是,该项目包含了前后台的完整源码,并经过严格调试,确保可以顺利运行。通过项目的学习和实践,您将能更好地掌握从后端到前端的完整开发流程,提升自己的编程技能。欢迎参考博主的详细文章或私信获取更多信息,利用这一宝贵资源来推进您的技术成长之路!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值