如题图,库函数里的数组最大数量都是指定为Integer.MAX_VALUE-8。按注释所说,8是为对象头预留的,对象头在64位虚拟机下占16个字节,8一定不是指字节数,如果指的是字长,那么这个数字应该是可以更小的。
找到分配数组的代码
typeArrayOop TypeArrayKlass::allocate_common(int length, bool do_zero, TRAPS) {
assert(log2_element_size() >= 0, "bad scale");
if (length >= 0) {
if (length <= max_length()) { //看这里 size_t size = typeArrayOopDesc::object_size(layout_helper(), length);
KlassHandle h_k(THREAD, this);
typeArrayOop t;
CollectedHeap* ch = Universe::heap();
if (do_zero) {
t = (typeArrayOop)CollectedHeap::array_allocate(h_k, (int)size, length, CHECK_NULL);
} else {
t = (typeArrayOop)CollectedHeap::array_allocate_nozero(h_k, (int)size, length, CHECK_NULL);
}
return t;
} else {
report_java_out_of_memory("Requested array size exceeds VM limit");
JvmtiExport::post_array_size_exhausted();
THROW_OOP_0(Universe::out_of_memory_error_array_size());
}
} else {
THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
}
}
可以注意到 length 大于max_length()的时候就会抛出Requested array size exceeds VM limit异常,而这个max_length是在初始化的时候设置的:
TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name) {
set_layout_helper(array_layout_helper(type));
assert(is_array_klass(), "sanity");
assert(is_typeArray_klass(), "sanity");
set_max_length(arrayOopDesc::max_array_length(type)); //看这里 assert(size() >= TypeArrayKlass::header_size(), "bad size");
set_class_loader_data(ClassLoaderData::the_null_class_loader_data());
}
计算过程:
static int32_t max_array_length(BasicType type) {
assert(type >= 0 && type < T_CONFLICT, "wrong type");
assert(type2aelembytes(type) != 0, "wrong type");
const size_t max_element_words_per_size_t =
align_size_down((SIZE_MAX/HeapWordSize - header_size(type)), MinObjAlignment);
const size_t max_elements_per_size_t =
HeapWordSize * max_element_words_per_size_t / type2aelembytes(type);
if ((size_t)max_jint < max_elements_per_size_t) {
// It should be ok to return max_jint here, but parts of the code
// (CollectedHeap, Klass::oop_oop_iterate(), and more) uses an int for
// passing around the size (in words) of an object. So, we need to avoid
// overflowing an int when we add the header. See CRs 4718400 and 7110613.
return align_size_down(max_jint - header_size(type), MinObjAlignment);
}
return (int32_t)max_elements_per_size_t;
}
而这个SIZE_MAX是一个较为巨大的数字:
#define SIZE_MAX UINT64_MAX#define UINT64_MAX 18446744073709551615ULL
所以最后都会落到这里:
return align_size_down(max_jint - header_size(type), MinObjAlignment);
此时:MinObjAlignment值为1,align_size_down这个宏可以忽略。
header_size即为对象头所占的字长(对齐/不对齐):
static int header_size(BasicType type) {
size_t typesize_in_bytes = header_size_in_bytes();
return (int)(Universe::element_type_should_be_aligned(type)
? align_object_offset(typesize_in_bytes/HeapWordSize)
: typesize_in_bytes/HeapWordSize);
}
基础类型只有Double和Long需要对齐:
inline bool Universe::element_type_should_be_aligned(BasicType type) {
return type == T_DOUBLE || type == T_LONG;
}
对齐的函数:(可以简单理解为字长+1取偶数部分)
#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1))
所以数组最大的大小即为:Integer.MAX_VALUE-对象头占的字长。
以64位开启压缩指针为例:markword占8个字节,klass指针4个字节,数组长度4个字节,一共是16个字节(两个)字长。
public class Hello {
public static void main(String[] args) {
Object[] o = new Object[Integer.MAX_VALUE-2];
}
}
运行:java -Xmx9000m -Xmn10m Hello,不会有任何异常。
假如关掉压缩指针,klass指针占8个字节,对象头一共8+8+4,再加上补齐,一共是3个字长,那么此时最大数组大小就是Integer.MAX_VALUE-3了。
$ java -Xmx9000m -Xmn10m -XX:-UseCompressedOops Hello
Exception in thread "main" java.lang.OutOfMemoryError: Requested array size exceeds VM limit
at Hello.main(Hello.java:4)
32位虚拟机数组的对象头:markword占4个字节,klass指针占4个字节,数组长度占4个字节,一共是3个字长。在不需要对齐的情况下数组最大最小为Integer.MAX_VALUE-3,而在需要对齐的情况下就是Integer.MAX_VALUE-4。(这一个没有验证,因为我没有装32位的jdk...)