java.lang之java.lang.String 源码阅读及分析

1,String对象在内存中所占内存大小

为了方便查找生成的String对象,编写以下测试代码

public class StringMain{
	
	public static void main(String[] args){
		StringTest t = new StringTest();
		print();
	}
	
	public static void print(){
		
	}
}

class StringTest{
	private String s1 = new String();
	
	private String s2 = new String("123");
}

其中 s1位空串,s2含三个字符,下面借助hotspot调试器来看下程序运行时 对象究竟占多大内存,本人的系统为32位


首先找到StringTest对象的地址0x3eb5658,s1对象在0x03eb5668,s2在0x03eb5690,先看下s1这个空串



String类中有三个成员变量

/** The value is used for character storage. */
    private final char value[];


    /** Cache the hash code for the string */
    private int hash; // Default to 0
    private transient int hash32 = 0;

对照着上图,0x03eb5668这个地址储存的是mark_word,0x03eb566c存储的是类型指针,0x03eb5670存储的是数组value指针,0x03eb5674存储hash,0x03eb5678存储hash32,最后一个填充

综上,一个String对象在内存中占24字节

2,subString方法

public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
    }
最终返回的是一个新的String 对象

3,replace方法

public String replace(char oldChar, char newChar) {
        if (oldChar != newChar) {
            int len = value.length;
            int i = -1;
            char[] val = value; /* avoid getfield opcode */

            while (++i < len) {
                if (val[i] == oldChar) {
                    break;
                }
            }
            if (i < len) {
                char buf[] = new char[len];
                for (int j = 0; j < i; j++) {
                    buf[j] = val[j];
                }
                while (i < len) {
                    char c = val[i];
                    buf[i] = (c == oldChar) ? newChar : c;
                    i++;
                }
                return new String(buf, true);
            }
        }
        return this;
    }
首先找到第一个符合条件的字符,将这个字符之前的都拷贝到数组中,后面的进行挨个比较替换。该方法可以换一种实现,首先将字符串变为数组,然后挨个比较替换 。


4,trim方法

public String trim() {
        int len = value.length;
        int st = 0;
        char[] val = value;    /* avoid getfield opcode */

        while ((st < len) && (val[st] <= ' ')) {
            st++;
        }
        while ((st < len) && (val[len - 1] <= ' ')) {
            len--;
        }
        return ((st > 0) || (len < value.length)) ? substring(st, len) : this;
    }
能够去掉字符串头尾的空白 。

5,public native String intern();

这是一个native方法,该方法有这样一段注释:

/**
     * When the intern method is invoked, if the pool already contains a
     * string equal to this <code>String</code> object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this <code>String</code> object is added to the
     * pool and a reference to this <code>String</code> object is returned.
     */
若常量池中已经有该String想象表示的内容,返回常量池中的string,否则将String对象放入到常量池,返回该string对象的引用,所以以下代码应该成立:

String s1 = new String("123");
		String ss1 = s1.intern();
		String s2 = "123";
		System.out.println(s1 == s2);//false
		System.out.println(ss1 == s2);//true
		
		String s3 = new String("1")+new String("1");
		String ss3 = s3.intern();
		String s4 = "11";
		System.out.println(s3 == s4);//true
		System.out.println(ss3==s4);//true

下面来看下这个native方法的实现,它的实现在String.c   

#include "jvm.h"
#include "java_lang_String.h"

JNIEXPORT jobject JNICALL
Java_java_lang_String_intern(JNIEnv *env, jobject this)
{
    return JVM_InternString(env, this);
}

它实际上的实现在jvm.cpp  

JVM_ENTRY(jstring, JVM_InternString(JNIEnv *env, jstring str))
  JVMWrapper("JVM_InternString");
  JvmtiVMObjectAllocEventCollector oam;
  if (str == NULL) return NULL;
  oop string = JNIHandles::resolve_non_null(str);
  oop result = StringTable::intern(string, CHECK_NULL);
  return (jstring) JNIHandles::make_local(env, result);
JVM_END
调用了StringTable的intern方法

oop StringTable::intern(Handle string_or_null, jchar* name,
                        int len, TRAPS) {
  unsigned int hashValue = hash_string(name, len);//计算hash
  int index = the_table()->hash_to_index(hashValue);//计算index
  oop found_string = the_table()->lookup(index, name, len, hashValue);//查找 

  // Found
  if (found_string != NULL) {
    ensure_string_alive(found_string); // Tell the GC that this string was looked up in the StringTable.
    return found_string;
  }

  debug_only(StableMemoryChecker smc(name, len * sizeof(name[0])));
  assert(!Universe::heap()->is_in_reserved(name),
         "proposed name of symbol must be stable");

  Handle string;
  // try to reuse the string if possible
  if (!string_or_null.is_null()) {
    string = string_or_null;
  } else {
    string = java_lang_String::create_from_unicode(name, len, CHECK_NULL);
  }

#if INCLUDE_ALL_GCS
  if (G1StringDedup::is_enabled()) {
    // Deduplicate the string before it is interned. Note that we should never
    // deduplicate a string after it has been interned. Doing so will counteract
    // compiler optimizations done on e.g. interned string literals.
    G1StringDedup::deduplicate(string());
  }
#endif

  // Grab the StringTable_lock before getting the_table() because it could
  // change at safepoint.
  oop added_or_found;
  {
    MutexLocker ml(StringTable_lock, THREAD);
    // Otherwise, add to symbol to table
    added_or_found = the_table()->basic_add(index, string, name, len,
                                  hashValue, CHECK_NULL);
  }

  ensure_string_alive(added_or_found);

  return added_or_found;
}
StringTable实际上是一个hashTable

class StringTable : public RehashableHashtable<oop, mtSymbol> {
}
当调用String.intern方法时,首先去StringTable查找,如果StringTable中有该String,返回StringTable中的String。

如果没有找到,将String添加到StringTable中,然后返回该String。


参考资料:

http://rednaxelafx.iteye.com/blog/1847971





  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值