文档章节

java string中的比较难注意细节(intern,subString和gc回收String)

xpbob
 xpbob
发布于 2017/02/27 23:18
字数 933
阅读 727
收藏 3

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

    jdk1.6后对字符串常量池做了改动,从源码到表现都做了很多改动。都是在规避了OOM的问题。下面说一些改动细节以及一些常见的错误认识。

subString的变动

1.6的实现如下

    public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > count) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        if (beginIndex > endIndex) {
            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
        }
        return ((beginIndex == 0) && (endIndex == count)) ? this :
            new String(offset + beginIndex, endIndex - beginIndex, value);
    }

        这段代码逻辑没有问题,但是最后一句话改良了。问题出在这个String的构造方法上。

    String(int offset, int count, char value[]) {
        this.value = value;
        this.offset = offset;
        this.count = count;
    }

        这里直接把偏移量,长度和char数组都记录下来。当这执行subString的字符串被GC时,此时的char[]就内存泄露了。因为char数组多余的部分本来应该被回收的。

1.7直接使用了其他构造方法

    public String substring(int beginIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        int subLen = value.length - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
    }

调用下面的构造方法

    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

直接调用下面这个构造方法,直接执行了拷贝操作,虽然多了一部操作,同时也消除了内存泄露的问题。

    这个点是从葛一鸣老师的书中看到的。很符合这个专题,详细的还请看老师的书。

intern的变动

    具体的我原来在博客中写了,可以看

String的1.7变动:https://my.oschina.net/xpbob/blog/751405

String的1.6的表现以及场景问题:https://my.oschina.net/xpbob/blog/746488

    主要变动就是字符串常量池的位置发生了改变,由方法区转化到了堆中。intern返回的是堆中的第一次出现且intern的字符串的String对象。

    intern的操作最后参考SymbolTable.cpp中的intern方法,1.6和1.7都是这个。

String的gc

    这个细节是上intern的变动发现的。

    最开始我的理解是字符串常量基本不会被回收,只会随着类的回收而回收。事实情况不是这样的。

	public static void main(String[] args) {
		show();
		System.gc();
		show();

	}
	
	public static void show(){
		String s =new String(new char[]{'a','b'});
		System.out.println(System.identityHashCode(s));
		System.out.println(System.identityHashCode(s.intern()));
	}

    上面这段代码最好在jdk1.7以上执行,效果比较明显,你会发现,show方法中字符串ab和常量池中的ab的地址是一样的(这里说object是hashcode基本等同于地址,虽然事实并不是,只是和重写的hash来做区分),如果没有执行gc,那么结果都是常量池中的ab总会和第一次新建的ab的地址保持一致。

    最开始我以为是gc的时候from到to区域,所以地址变了,仔细调试看了唯一Id发现不是。

    intern的字符串被gc掉了。因为最初网上有用Intern来把字符串放入常量池来减少保证地址比较相同的做法。这个做法没什么问题。只是如果要保存地址值来做标识,那就会出问题了。

    回收的代码,1.6和1.7也是有点不同的。

    1.6的代码在SymbolTable.hpp中的unlink方法。

    1.7的代码在SymbolTable.cpp中的unlink方法。

    都是在gc执行的时候操作。只要没有堆栈对这个string持有引用,就可以被回收,这样就避免了太多的字符串加入到常量池中,把常量池所在的区域给oom了。

 

 

© 著作权归作者所有

xpbob

xpbob

粉丝 99
博文 107
码字总数 84788
作品 0
高级程序员
私信 提问
加载中

评论(0)

java8中字符串常量以及GC相应处理机制

[TOC] 1,常量池 1.1, class文件常量池 class文件常量池位于class文件中 class文件头4个字节称为魔数,魔数后面的4个字节为文件版本号,而版本号之后的就是常量池的入口。该常量池用于存放编...

osc_5v9u1t19
2019/08/31
2
0
深入理解 String, StringBuffer 与 StringBuilder 的区别

String 字符串常量 StringBuffer字符串变量(线程安全) StringBuilder字符串变量(非线程安全) 简要的说, String 类型和StringBuffer类型的主要性能区别其实在于 String 是不可变的对象,...

大数据之路
2013/01/16
2.4K
0
JDK源码学习笔记——String

1、学习jdk源码,从以下几个方面入手:   类定义(继承,实现接口等)   全局变量   方法   内部类 2、hashCode   private int hash;   public int hashCode() {   } 为什么是...

osc_cjbg0415
2018/10/18
1
0
聊聊Java String.intern 背后你不知道的知识

Java的 String类有个有意思的public方法: public String intern() 这个功能为String提供了字符串池,我们可以使用它来优化内存。 但是,这有一个缺点:在OpenJDK中,String.intern()是本地方...

osc_jtynr6ek
2019/05/16
1
0
JVM-String常量池与运行时常量池

Start with JVM 周志明先生著-《深入理解Java虚拟机》,书买回来好几天了,但是最近才准备开始搞一搞了(哭瞎…..)。首先是第一章的Java以及JVM发展历史,大概知道了现行的应用最广泛的Jav...

osc_vg6s3gcq
2018/07/17
2
0

没有更多内容

加载失败,请刷新页面

加载更多

Kubernetes Ingress简单入门

本文转载自Rancher Labs 不知道你是否注意到一个奇怪的现象,尽管Kubernetes Ingress API仍然处于beta状态,但是已经有许多公司使用它来暴露Kubernetes服务。从事相关项目的工程师表示,Kub...

osc_slfjdpj6
36分钟前
21
0
ACCDB MDB Explorer for mac(数据库查看器)

想要更加直观的查看大数据,小编推荐使用mac数据查看工具ACCDB MDB Explorer Mac版,该软件能够将导入的数据库导出为XML,XLS,CSV和TXT格式的文件简便高效请添加链接描述,功能强大,需要的...

osc_63i522q0
38分钟前
14
0
二 javanio中的Channel transfer 内容

java nio中channel操作 分散 将channel中读取的数据分散写入多个buffer中 聚集 将多个buffer写入数据到同一个channel 应用 将消息头和消息体放入到不同buffer,便于分类处理应用 将消息头和消...

writeademo
38分钟前
26
0
如何更好的盘活和用好数据资产,企业如何构建数据竞争力的关键在这里!

在今年4月公布的《中共中央国务院关于构建更加完善的要素市场化配置体系机制的意见》中,“数据”首次作为一种新型生产要素写入中央文件中,与土地、劳动力、资本、技术等传统要素并列成为要...

osc_5asswvor
38分钟前
30
0
如何更好的盘活和用好数据资产,企业如何构建数据竞争力的关键在这里!

在今年4月公布的《中共中央国务院关于构建更加完善的要素市场化配置体系机制的意见》中,“数据”首次作为一种新型生产要素写入中央文件中,与土地、劳动力、资本、技术等传统要素并列成为要...

osc_e7clsgkf
39分钟前
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部