1

我正在尝试使用 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
4

2 回答 2

4

这是一个例子。确保使用 unsafe 尽可能安全,因为如果您错过了 C 编程时代的设置错误,您可以使用 unsafe....

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

于 2014-03-21T08:55:42.783 回答
2

一些注意事项:

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

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

于 2013-10-30T21:23:04.023 回答