高效使用内存,这些你得知道

简介: 堆内存用的越少,堆被填满的几率就越低,新生代回 收的次数更少,对象的晋升年龄也就不会很频繁地增加,这意味着对象被提升到老年代的 可能性也降低了,因此, Full GC会减少,降低了GC发生的频率「下面将介绍几种减少内存使用的方式」对象会占用一定数量的堆内存,所以要减少内存使用,最简单的方式就是让对象小一些减少对象大小有两种方式:减少实例变量的个数,或者减少实例变量的大小注意这里的减少变量大小主要是对每个对象选择更小的类型,避免空间的浪费「同时对于对象大小的计算,有几个注意点:」1.对象大小未必和你计算的一样,因为对象会被填充到 8 字节的整数倍2.对象内部即使为 null 的实例变量也会占用

之后更新文章的频率会变高,不过可能不会只更新技术文章了

最近也在阅读“金字塔原理“和”高效能人士的七个习惯“这两本书,之后也会做个读书心得分享

前言

首先有一个结论:

「减少堆内存的使用可以更高效使用内存」

这句话怎么理解呢?

堆内存用的越少,堆被填满的几率就越低,新生代回 收的次数更少,对象的晋升年龄也就不会很频繁地增加,这意味着对象被提升到老年代的 可能性也降低了,因此, Full GC会减少,降低了GC发生的频率

「下面将介绍几种减少内存使用的方式」


减少对象大小

对象会占用一定数量的堆内存,所以要减少内存使用,最简单的方式就是让对象小一些

减少对象大小有两种方式:减少实例变量的个数,或者减少实例变量的大小

注意这里的减少变量大小主要是对每个对象选择更小的类型,避免空间的浪费

「同时对于对象大小的计算,有几个注意点:」

1.对象大小未必和你计算的一样,因为对象会被填充到 8 字节的整数倍

2.对象内部即使为 null 的实例变量也会占用空间


延迟初始化

有时候需要采用延迟初始化来降低初始化类和创建对象的开销

注意我们一般不会在线程安全的代码上引入延迟初始化,这样会增加同步操作的开销

同时对于使用了线程安全对象的代码,如果要采用延迟初始化,也应该使用双重检查锁,比如大家熟悉的单例的写法:

public class LazyDoubleCheckSingleton {
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null;
    private LazyDoubleCheckSingleton(){
    }
    public static LazyDoubleCheckSingleton getInstance() {
        if (lazyDoubleCheckSingleton == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                if (lazyDoubleCheckSingleton == null) {
                    lazyDoubleCheckSingleton =  new LazyDoubleCheckSingleton();
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }
}

「尽早清理空值数据」

延迟初始化的对象如果一直不用的话,通过将变量的值设置为 null ,实现尽早清理, 从而使对象可以更快地被垃圾收集器回收

可以看看JDK 中 ArrayList 类的 remove() 方法的实现:

public E remove(int index) {
        //检查下标是否越界
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(index);
        //将index+1以及之后的元素向前移动以为,覆盖被删除的值
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        //将最后一个位置的元素清空
        elementData[--size] = null; // clear to let GC do its work
        return oldValue;
    }

关键就是对于不需要再引用的元素,应该主动将其设置为 null


不可变对象

「使用不可变的对象常量可以很好的减少空间的浪费」

在 Java 中,很多对象类型都是不可变的

包括那些有相应的基本类型的类,如 Integer 、 Double 和 Boolean 等, 以及其他一些基于数值的类型, 如 BigDecimal

最常见的 Java 对象当属不可变的 String

「举个Boolean类的例子说说」

在 Java 中,其实只需要两个 Boolean 示例,一个表 示 true , 一个表示 false

Boolean 类有一个 public 的构造 器,应用喜欢创建多少这类对象就能创建多少,即使它们和两个Boolean 对象其 中之一是完全相同的

更好的设计方案应该是,让 Boolean 类只有一个 private 的构造器, 通过 static 方法根据其参数返回 Boolean.TRUEBoolean.FALSE ,就可以防止它们占用应用中额外的堆空间

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);


字符串常量池

字符串是最常见的 Java 对象;应用的堆中几乎到处都是字符串

如果有大量的字符串是相同的,那很大一部分空间都是浪费的

「所以有了字符串常量池的概念」

我们知道String 常见的创建方式有两种,new String() 的方式和直接赋值的方式

直接赋值的方式会先去字符串常量池中查找是否已经有此值,如果有则把引用地址直接指向此值,否则会先在常量池中创建,然后再把引用指向此值;而 new String()的方式一定会先在堆上创建一个字符串对象,然后再去常量池中查询此字符串的值是否已经存在,如果不存在会先在常量池中创建此字符串,然后把引用的值指向此字符串

String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true


除此之外编译器还会对 String 字符串做一些优化,例如以下代码:

String s1 = "Ja" + "va";
String s2 = "Java";
System.out.println(s1 == s2);

虽然 s1 拼接了多个字符串,但对比的结果却是 true,我们使用反编译工具,看到的结果如下:

Compiled from "StringExample.java"
public class com.lagou.interview.StringExample {
  public com.lagou.interview.StringExample();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
    LineNumberTable:
      line 3: 0
  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String Java
       2: astore_1
       3: ldc           #2                  // String Java
       5: astore_2
       6: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: aload_1
      10: aload_2
      11: if_acmpne     18
      14: iconst_1
      15: goto          19
      18: iconst_0
      19: invokevirtual #4                  // Method java/io/PrintStream.println:(Z)V
      22: return
    LineNumberTable:
      line 5: 0
      line 6: 3
      line 7: 6
      line 8: 22
}

从编译代码 #2 可以看出,代码 “Ja”+”va” 被直接编译成了 “Java” ,因此 s1==s2 的结果才是 true,这就是编译器对字符串优化的结果


最后

「喜欢的话,希望帮忙点赞,转发下哈,谢谢」

相关文章
|
数据挖掘 Python 数据采集
带你读《Python金融大数据挖掘与分析全流程详解》之三:金融数据挖掘案例实战1
本书以功能强大且较易上手的Python语言为编程环境,全面讲解了金融数据的获取、处理、分析及结果呈现。全书共16章,内容涉及Python基础知识、网络数据爬虫技术、数据库存取、数据清洗、数据可视化、数据相关性分析、IP代理、浏览器模拟操控、邮件发送、定时任务、文件读写、云端部署、机器学习等,可以实现舆情监控、智能投顾、量化金融、大数据风控、金融反欺诈模型等多种金融应用。
|
弹性计算 网络协议 安全
安全组规则
安全组规则
500 3
|
10月前
|
弹性计算 Linux 云计算
阿里云操作系统控制台——ECS操作及云计算应用实践
本文详细介绍了云服务器ECS的使用流程,包括开通服务、系统配置、权限管理、组件安装及内存全景诊断等关键步骤。通过开通阿里云操作系统服务、授予RAM用户权限和安装必要组件,可实现对服务器的有效管理与维护。在内存诊断部分,展示了如何发起诊断并解析结果,帮助精准定位内存问题。此外,文章还讲解了利用ECS训练模型的操作方法,从上传文件到终端命令执行,直至完成模型训练。最后总结指出,掌握这些技能不仅提升了对云服务器架构的理解,还为实际业务提供了高效解决方案,展现了ECS在数据处理与分析中的重要价值。
阿里云操作系统控制台——ECS操作及云计算应用实践
|
机器学习/深度学习 人工智能 算法
探索机器学习中的支持向量机(SVM)算法
【5月更文挑战第27天】在数据科学和人工智能的领域中,支持向量机(SVM)是一种强大的监督学习模型,它基于统计学习理论中的VC维理论和结构风险最小化原理。本文将详细介绍SVM的工作原理、核心概念以及如何在实际问题中应用该算法进行分类和回归分析。我们还将讨论SVM面临的挑战以及如何通过调整参数和核技巧来优化模型性能。
|
人工智能 自然语言处理 关系型数据库
客户说|宝宝树选用AnalyticDB RAG引擎,共创智能母婴生活新范式
宝宝树与阿里云深度合作,利用大数据和AI技术,推出了一系列智能化产品,如AI解读B超单、AI起名等,覆盖备孕、孕期、产后等场景,提升了用户体验,推动了商业化进程。通过技术架构的优化,宝宝树在内容生产和搜索精度上取得了显著成效,未来将继续深化“AI+母婴”战略,为用户提供更全面、个性化的服务。
|
安全 数据可视化 物联网
智慧园区解决方案:科技赋能,打造未来管理新典范
智慧园区作为城市发展的重要组成部分,借助5G、云计算、大数据、物联网等前沿技术,实现高效、活力、绿色、安全的四大核心目标。通过全场景数字化感知、统一数据模型构建、智能化管控与数据化运营、综合安防管理等手段,提升园区运营效率和管理水平,促进产业升级和可持续发展。
818 11
|
机器学习/深度学习 自然语言处理 搜索推荐
云上智能客服机器人:重塑客户服务体验的新篇章
未来,云上智能客服机器人将继续深化深度学习技术的应用,通过跨领域的知识融合和模型训练提升其在复杂场景下的理解和决策能力。同时,机器人将更加注重多模态交互技术的发展以提供更加自然流畅的交互体验。 4.2 情感智能与人性化服务 随着情感智能技术的不断发展,云上智能客服机器人将更加注重情感交互和人性化服务。机器人将能够识别用户的情感状态和需求偏好提供更加贴心和温暖的服务体验。
836 7
|
存储 弹性计算 安全
阿里云服务器4核8G配置可选实例规格、收费标准及2024年优惠价格
阿里云服务器4核8G配置多少钱?可选实例规格有哪些?2024年的优惠价格是多少?根据阿里云2024年的收费标准及活动价格来看,4核8G配置云服务器的价格为199元1年。阿里云服务器4核8G配置可选的规格有二十几个,不同实例的价格有所不同,下面是4核8G配置可选实例规格详解及优惠价格表。
1044 1
阿里云服务器4核8G配置可选实例规格、收费标准及2024年优惠价格
|
存储 运维 监控
强安全、免运维、轻资产,无影云电脑助力企业构建云上安全办公空间
无影云电脑助力企业构建云端一体、安全高效的一站式云上办公工作空间,为企业的云上创新护航。
738 1
|
前端开发 JavaScript UED
深度解析Qt背景设计:从基础到高级,从Widget到Quick(三)
深度解析Qt背景设计:从基础到高级,从Widget到Quick
587 0

热门文章

最新文章