代码精进之路
范学雷
Oracle首席软件工程师,Java SE安全组成员,OpenJDK评审成员
立即订阅
6353 人已学习
课程目录
已完结 47 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 你写的每一行代码,都是你的名片
免费
第一模块:代码“规范”篇 (16讲)
01 | 从条件运算符说起,反思什么是好代码
02 | 把错误关在笼子里的五道关卡
03 | 优秀程序员的六个关键特质
04 | 代码规范的价值:复盘苹果公司的GoToFail漏洞
05 | 经验总结:如何给你的代码起好名字?
06 | 代码整理的关键逻辑和最佳案例
07 | 写好注释,真的是小菜一碟吗?
08 | 写好声明的“八项纪律”
09 | 怎么用好Java注解?
10 | 异常处理都有哪些陷阱?
11 | 组织好代码段,让人对它“一见钟情”
12丨组织好代码文件,要有“用户思维”
13 | 接口规范,是协作的合约
14 | 怎么写好用户指南?
15 | 编写规范代码的检查清单
16丨代码“规范”篇用户答疑
第二模块:代码“经济”篇 (14讲)
17 | 为什么需要经济的代码?
18丨思考框架:什么样的代码才是高效的代码?
19 | 怎么避免过度设计?
20 | 简单和直观,是永恒的解决方案
21 | 怎么设计一个简单又直观的接口?
22丨高效率,从超越线程同步开始!
23 | 怎么减少内存使用,减轻内存管理负担?
24 | 黑白灰,理解延迟分配的两面性
25 | 使用有序的代码,调动异步的事件
26 | 有哪些招惹麻烦的性能陷阱?
27 | 怎么编写可持续发展的代码?
28 | 怎么尽量“不写”代码?
29 | 编写经济代码的检查清单
30丨“代码经济篇”答疑汇总
第三模块:代码“安全”篇 (14讲)
31 | 为什么安全的代码这么重要?
32 | 如何评估代码的安全缺陷?
33 | 整数的运算有哪些安全威胁?
34 | 数组和集合,可变量的安全陷阱
35 | 怎么处理敏感信息?
36 | 继承有什么安全缺陷?
37 | 边界,信任的分水岭
38 | 对象序列化的危害有多大?
39 | 怎么控制好代码的权力?
40 | 规范,代码长治久安的基础
41 | 预案,代码的主动风险管理
42 | 纵深,代码安全的深度防御
43 | 编写安全代码的最佳实践清单
44 | “代码安全篇”答疑汇总
加餐 (1讲)
Q&A加餐丨关于代码质量,你关心的那些事儿
结束语 (1讲)
结束语|如何成为一个编程好手?
代码精进之路
登录|注册

23 | 怎么减少内存使用,减轻内存管理负担?

范学雷 2019-02-25
管理内存,不管是什么编程语言,向来都是一个难题。Java 语言能够长期领先的一个重要原因,就是它拥有强大的内存管理能力,并且这种能力还在不断地进化。然而,只依靠 Java 内在的内存管理能力,是远远不够的。
2018 年 9 月,亚马逊向 OpenJDK 社区提交了一个改进请求。这个改进涉及到一个问题,如果一个服务的缓存数量巨大,比如说有 10 万个连接会话,Java 的垃圾处理器要停滞几分钟,才能清理完这么巨大的缓存。而这几分钟的停滞,是不可忍受的事故。
这是一个值得我们关注的细节。缓存的本意,就是为了提高效率。然而,拥有过多的用户,过多的缓存,反而会让效率变低。
随着大数据、云计算以及物联网的不断演进,很多技术都面临着巨大的挑战。七八年前(2010 年左右),能解决 C10K(同时处理 1 万个用户连接)问题,感觉就可以高枕无忧了。现在有不少应用,需要开始考虑 C10M(同时处理 1 千万个用户连接)问题,甚至是更多的用户连接,以便满足用户需求。很多以前不用担心的问题,也会冒出来算旧账。
要想让内存使用得更有效率,我们还需要掌握一些成熟的实践经验。

使用更少的内存

提高内存使用最有效率的办法,就是使用更少的内存。这听起来像是废话,却也是最简单直接、最有用的办法。减少内存的使用,意味着更少的内存分配、更少的内存填充、更少的内存释放、更轻量的垃圾回收。内存的使用减少一倍,代码的效率会成倍地提升,这不是简单的线性关系。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《代码精进之路》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(12)

  • pyhhou
    仅仅在这个代码基础上改的话:
    public HelloWords getInstance(String language) {
          if (language.equals(ENGLISH.language)) {
                return ENGLISH;
          }
          
          if (language.equals(MANDARIN.language)) {
               return MANDARIN;
          }
          
          throw IllegalArgumentException("This language doesn't exist");
    }

    用 private 来定义构造方法是为了防止类的使用者通过 new 的方式来产生新的对象,在这样的方式下,类的使用者只能通过类中的 static 对象中的 getInstance 方法来获取已经存在的对象,从而减轻了内存管理的负担

    不用 private 来定义变量是因为类的使用者通过 getInstance 方法只能获取类的实例,也就是对象,但是对于对象里面的内容(language、greeting)还需进一步的获取; 用 final 去定义这些变量是为了防止类的使用者更改 static 对象中的内容,因为更改后会导致内容发生全局性的改变,从而和对象本身不匹配

    但是个人认为这样的设计并没有很好的体现我们之前讲的 “简单直观” 的理念,首先这里存在着很深的嵌套,就是:类 -> static 对象 -> getInstance 方法 -> static 对象 -> 变量,这么分析看来设计并不直观,对于我来说,刚开始理解还比较的困难,所以可以考虑前面的将这里的 getInstance 方法改为 static 的方法,另外就是可以设一个 getGreeting 的方法去取得 instance 中的内容,用户通过函数 API 的方式获取内部信息而不是直接获取,在这种情况下我们可以将变量设为 private,这样比较好的隐藏内部实现,当然我们也可以使用之前提到的 enum 去实现:
    static enum Hellowords {
         ENGLISH ("English", "Hello"),
         MANDARIN ("Ni Hao");
        
         final String language;
         final String greeting;

         private HelloWords(String language, String greeting) {
               this.language = language;
               this.greeting = greeting;
         }
    }

    作者回复: 你的留言里,有大量的深入思考,要点赞!

    2019-03-11
    6
  • 轻歌赋
    因为这是final修饰的变量,又是string,属性不可改变
    可以考虑改成enum,或者是单例模式
    单例模式推荐内部静态类持有的方式,相对更简单
    如果使用容器化管理,例如spring,可以考虑注册成两个bean,通过规范约束不得创建,但是编程层面的约束力相对薄弱

    作者回复: 嗯,可以考虑使用内部静态类持有的方式。

    2019-02-25
    3
  • 君不得闲
    public static HelloWords getInstance(String language){
            if ("English".equals(language)) {
                return ENGLISH;
            }

            if ("Mandarin".equals(language)) {
                return MANDARIN;
            }

            throw new IllegalStateException("This language is not supported");
        }

    构造方法没有实例化是为了避免使用者调用生成多实例,违背设计减少实例数量的初衷。
    至于变量为什么不使用private关键字百思不得其解。因为如果没有修饰符修饰就代表只要是相同包内都可以对该类生成的实例的变量进行直接修改,但是final关键字又对这种修改做了限制,说明这两个是不可修改的常量,因为没有static修饰,说明这个常量是实例私有的,个人想法是已经用final了就没有必要用private修饰的多此一举了,就像接口的方法不用特意声明public abstract一样。

    作者回复: 异常类型使用IllegalArgumentException或者一个检查型异常,会好一点点。

    2019-02-25
    2
  • Geek_28d7fe
    单例模式破坏类的封装,现在很多开源代码尽量避免使用单例,是不是能不用单例就不用单例

    作者回复: 很多场景下,单例模式是优先选择。 比如enum就是单例模式的典范。所以,要看具体的场景,具体的状况。

    2019-05-21
    1
  • 天佑
    在编写代码时,如果能够引用,就坚决不要拷贝,老师您好,这样子做不会出现安全问题么,那入参防御性复制的使用场景是什么,有点糊涂了,期待老师答疑。

    作者回复: 有安全问题,就不算可以引用了。什么时候防御性复制,什么时候不用复制,我们在第三部分还会说这个问题。

    2019-03-11
    1
  • 往事随风,顺其自然
    构造器使用私有的,不允许实例化多个类,减少实例数量,变量没有私有化是因为string 不可变的类。
    2019-02-25
    1
  • sovran
    还有一种方法,复用同一块内存,减少不必要的内存申请释放。 程序开始就申请一块全局的内存,在软件运行的不同的阶段存储不一样的变量。 比如C语言的union结构。
    2019-12-08
  • 李米
    最好的优化就是不用,不用线程同步,不使用内存。老师的风格让我想起来奇葩说里的李诞:名画的最好归宿就是烧了。~老师技术不但实力强,还是个逗逼
    2019-12-07
  • Sisyphus235
    内存管理这里说的大多是 Java 的语境,能够给一些通用的经验,如何在其他语言和框架下处理,而不是使用特定的包或者语言工具?

    作者回复: 这些原则同样适用于其他语言。你可以试试把它们运用到C++/C/GoLang等其他语言.

    2019-05-23
  • 天佑
    “在编写代码时,如果能够引用,就坚决不要拷贝”
    这里不适用公共接口设计场景吧,考虑可变量的危害。

    作者回复: 是的。这里我加了个限定词“如果能够引用”,读到后面的文章,就知道这个限定词的作用了。外部接口,可变量要遵循审慎的原则,尽量拷贝或者规范明确标明不拷贝;内部实现的代码,如果清楚可变量的传递和变化不会捣乱,使用引用就行。

    2019-04-14
  • 徐题
    final修饰的类,它的成员变量也是final的吗?

    作者回复: 不是,除非成员变量使用了final修饰符。修饰符修饰什么,就对什么起作用,不要扩大范围。

    2019-03-06
  • 草原上的奔跑
    单例模式用枚举来实现比较好,不用考虑多线程同步问题,JVM会处理好。关于类的成员变量没有限定修饰符,那就是包共享的,用final修饰,则初始化后不可更改,String又是一个immutable的类,则成员变量可以当成常量在包中共享,且无并发问题。
    2019-03-02
收起评论
12
返回
顶部