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