使用Unsafe编写和读取字符串

时间:2013-10-30 19:30:31

标签: java

我正在尝试使用sun.misc.Unsafe将String写入内存,然后我正在尝试将它们读回来,我的行为很奇怪。我总是正确地得到字符串的最后一个字符。

public String getId() {
    long idofset = objectOffset + this.id;
    int l = unsafe.getByte(idofset);
    StringBuilder temp = new StringBuilder();
    for (long i = idofset + 1 ; i < idofset + l + 1; i++) {
        temp.append(unsafe.getChar(i));
        System.out.println("reading:  " + unsafe.getChar(i)  + " at address: " + i);
    }
    return temp.toString();
}

@Override
public void setId(String id) {
    long idofset = objectOffset + this.id;
    int length = Math.min(id.length(), 14);
    unsafe.putByte(idofset, (byte)length );
    for (long i = 0; i < length; i++) {
        System.out.println("writing:  " + id.charAt( (int)i)  + " at address: " + (idofset + i + 1));
        unsafe.putChar(idofset + i + 1 , id.charAt( (int)i ));
    }
}

,输出

writing:  0 at address: 27297681
writing:  . at address: 27297682
writing:  0 at address: 27297683
writing:  4 at address: 27297684
writing:  6 at address: 27297685
writing:  6 at address: 27297686
writing:  5 at address: 27297687
writing:  7 at address: 27297688
writing:  0 at address: 27297689
writing:  0 at address: 27297690
writing:  1 at address: 27297691
writing:  4 at address: 27297692
writing:  8 at address: 27297693
writing:  9 at address: 27297694
0.04665700148936924
--------read-------------
reading:  ? at address: 27297681
reading:  ? at address: 27297682
reading:  ? at address: 27297683
reading:  ? at address: 27297684
reading:  ? at address: 27297685
reading:  ? at address: 27297686
reading:  ? at address: 27297687
reading:  ? at address: 27297688
reading:  ? at address: 27297689
reading:  ? at address: 27297690
reading:  ? at address: 27297691
reading:  ? at address: 27297692
reading:  ? at address: 27297693
reading:  9 at address: 27297694
?????????????9

2 个答案:

答案 0 :(得分:3)

这是一个例子。一定要尽可能安全,不安全,因为如果你错过了C编程日的设置错误,你可以用不安全的方式重新安排它们....

public static final Unsafe UNSAFE;
public static final long STRING_VALUE_FIELD_OFFSET;
public static final long STRING_OFFSET_FIELD_OFFSET;
public static final long STRING_COUNT_FIELD_OFFSET;
public static final boolean ENABLED;

private static final boolean WRITE_TO_FINAL_FIELDS = Boolean.parseBoolean(System.getProperty("org.boon.write.to.final.string.fields", "true"));
private static final boolean DISABLE = Boolean.parseBoolean(System.getProperty("org.boon.faststringutils", "false"));

private static Unsafe loadUnsafe() {
    try {
        Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
        unsafeField.setAccessible(true);
        return (Unsafe) unsafeField.get(null);

    } catch (NoSuchFieldException e) {
        return null;
    } catch (IllegalAccessException e) {
        return null;
    }
}

static {
    UNSAFE = DISABLE ? null : loadUnsafe();
    ENABLED = UNSAFE != null;
}

private static long getFieldOffset(String fieldName) {
    if (ENABLED) {
        try {
            return UNSAFE.objectFieldOffset(String.class.getDeclaredField(fieldName));
        } catch (NoSuchFieldException e) {
            // field undefined
        }
    }
    return -1L;
}

static {
    STRING_VALUE_FIELD_OFFSET = getFieldOffset("value");
    STRING_OFFSET_FIELD_OFFSET = getFieldOffset("offset");
    STRING_COUNT_FIELD_OFFSET = getFieldOffset("count");
}

private enum StringImplementation {
    DIRECT_CHARS {
        @Override
        public char[] toCharArray(String string) {
            return (char[]) UNSAFE.getObject(string, STRING_VALUE_FIELD_OFFSET);
        }

        @Override
        public String noCopyStringFromChars(char[] chars) {
            if (WRITE_TO_FINAL_FIELDS) {
                String string = new String();
                UNSAFE.putObject(string, STRING_VALUE_FIELD_OFFSET, chars);
                return string;
            } else {
                return new String(chars);
            }
        }
    },
    OFFSET {
        @Override
        public char[] toCharArray(String string) {
            char[] value = (char[]) UNSAFE.getObject(string, STRING_VALUE_FIELD_OFFSET);
            int offset = UNSAFE.getInt(string, STRING_OFFSET_FIELD_OFFSET);
            int count = UNSAFE.getInt(string, STRING_COUNT_FIELD_OFFSET);
            if (offset == 0 && count == value.length)
                // no need to copy
                return value;
            else
                return string.toCharArray();
        }

        @Override
        public String noCopyStringFromChars(char[] chars) {
            if (WRITE_TO_FINAL_FIELDS) {
                String string = new String();
                UNSAFE.putObject(string, STRING_VALUE_FIELD_OFFSET, chars);
                UNSAFE.putInt(string, STRING_COUNT_FIELD_OFFSET, chars.length);
                return string;
            } else {
                return new String(chars);
            }
        }
    },
    UNKNOWN {
        @Override
        public char[] toCharArray(String string) {
            return string.toCharArray();
        }

        @Override
        public String noCopyStringFromChars(char[] chars) {
            return new String(chars);
        }
    };

    public abstract char[] toCharArray(String string);
    public abstract String noCopyStringFromChars(char[] chars);
}

public static StringImplementation STRING_IMPLEMENTATION = computeStringImplementation();

private static StringImplementation computeStringImplementation() {

    if (STRING_VALUE_FIELD_OFFSET != -1L) {
        if (STRING_OFFSET_FIELD_OFFSET != -1L && STRING_COUNT_FIELD_OFFSET != -1L) {
            return StringImplementation.OFFSET;

        } else if (STRING_OFFSET_FIELD_OFFSET == -1L && STRING_COUNT_FIELD_OFFSET == -1L) {
            return StringImplementation.DIRECT_CHARS;
        } else {
            // WTF
            return StringImplementation.UNKNOWN;
        }
    } else {
        return StringImplementation.UNKNOWN;
    }
}

public static boolean hasUnsafe() {
    return ENABLED;
}

public static final char [] EMPTY_CHARS = new char[0];
public static final String EMPTY_STRING = "";

public static char[] toCharArray(final String string) {
    if (string == null) return EMPTY_CHARS;
    return STRING_IMPLEMENTATION.toCharArray(string);

}

public static char[] toCharArrayNoCheck(final CharSequence charSequence) {
    return toCharArray(charSequence.toString());
}

public static char[] toCharArray(final CharSequence charSequence) {
    if (charSequence == null) return EMPTY_CHARS;
    return toCharArray(charSequence.toString());
}

public static char[] toCharArrayFromBytes(final byte[] bytes, Charset charset) {
    return toCharArray(new String(bytes, charset != null ? charset : StandardCharsets.UTF_8));
}

public static String noCopyStringFromChars(final char[] chars) {
    if (chars==null) return EMPTY_STRING;
    return STRING_IMPLEMENTATION.noCopyStringFromChars(chars);
}


public static String noCopyStringFromCharsNoCheck(final char[] chars) {
    return STRING_IMPLEMENTATION.noCopyStringFromChars(chars);
}

以上是来自Boon。

https://github.com/RichardHightower/boon

答案 1 :(得分:2)

一些注意事项:

  • char大小为两个字节,因此您的地址应该更改为两个而不是一个。一次添加一个,意味着你要覆盖每个字符两次,只剩下最后一个字符未被破坏。
  • 如果您想要低级别,可以使用Unsafe.copyMemory复制字符;)

如果你想看到一个广泛使用Unsafe的库https://github.com/OpenHFT/Chronicle-Bytes这个序列化使用BytesMarshallable接口来进行数据结构,并使用Unsafe访问堆内存来访问文本和二进制文件。这包括将double作为文本写入和读取到本机内存并解析它。