Skip to main content

100 posts tagged with "java"

View All Tags

java unsafe

· 2 min read

背景

java的unsafe包是有很多底层的api暴露出来,举例,java的netty就大量使用这个api

例子

下面是java的unsafe包里面的allocateMemory方法.

public class UnsafeDemo {

public static void main(String[] args) {
var unsafe = getUnsafe();
var memory = unsafe.allocateMemory(100);
System.out.println(memory);
}

private static Unsafe getUnsafe() {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
return (Unsafe) field.get(null);
} catch (Exception e) {
return null;
}
}
}

这里memory 返回的是一个地址

实际上是调用Unsafe_AllocateMemory0

UNSAFE_ENTRY(jlong, Unsafe_AllocateMemory0(JNIEnv *env, jobject unsafe, jlong size)) {
size_t sz = (size_t)size;

assert(is_aligned(sz, HeapWordSize), "sz not aligned");

void* x = os::malloc(sz, mtOther);

return addr_to_java(x);
} UNSAFE_END

最后调用的是glibc 的malloc

void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {

// Special handling for NMT preinit phase before arguments are parsed
void* rc = NULL;
if (NMTPreInit::handle_malloc(&rc, size)) {
// No need to fill with 0 because DumpSharedSpaces doesn't use these
// early allocations.
return rc;
}

DEBUG_ONLY(check_crash_protection());

// On malloc(0), implementations of malloc(3) have the choice to return either
// NULL or a unique non-NULL pointer. To unify libc behavior across our platforms
// we chose the latter.
size = MAX2((size_t)1, size);

// For the test flag -XX:MallocMaxTestWords
if (has_reached_max_malloc_test_peak(size)) {
return NULL;
}

const size_t outer_size = size + MemTracker::overhead_per_malloc();

// Check for overflow.
if (outer_size < size) {
return NULL;
}

ALLOW_C_FUNCTION(::malloc, void* const outer_ptr = ::malloc(outer_size);) <-- malloc 分配内存
if (outer_ptr == NULL) {
return NULL;
}

void* const inner_ptr = MemTracker::record_malloc((address)outer_ptr, size, memflags, stack);

if (DumpSharedSpaces) {
// Need to deterministically fill all the alignment gaps in C++ structures.
::memset(inner_ptr, 0, size);
} else {
DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);)
}
DEBUG_ONLY(break_if_ptr_caught(inner_ptr);)
return inner_ptr;
}

相关阅读

each jvm bytecode implement in x86 with asm

· 7 min read

背景

想要了解jvm的bytecode 的汇编实现 ,目标平台是x86

汇编格式

同样一个汇编语句:将1赋值给rax

汇编有两种表达方式

desc/描述intelAT&T
将1写入rax寄存器mov eax,1movl $1,%eax
将rab+3 的地址的值写入raxmov eax,[ebx+3]movl 3(%ebx),%eax

stack frame

在x86 64 位的模式下 rbcp 是用r13 , 描述的是下一个指令,i = instruction
r14则存了本地变量指针

// Global Register Names
static const Register rbcp = LP64_ONLY(r13) NOT_LP64(rsi);
static const Register rlocals = LP64_ONLY(r14) NOT_LP64(rdi);

这里LP64_ONLY()和NOT_LP64()是通过宏_LP64来确定的

__LP64__
_LP64
These macros are defined, with value 1, if (and only if) the compilation is for a target where long int and pointer both use 64-bits and int uses 32-bit.

amd64 下面的寄存器

java的stack frame

寄存器含义、描述
r14存了本地变量的基地址
r13指向下一个执行的bytecode

类似c的堆栈,java 的栈如下:

stack

相关阅读

frame 用下面的结构描述

主要包括:

  • _sp :指向栈
  • _pc : 指向指令
jdk/src/hotspot/share/runtime/frame.hpp
class frame {
private:
// Instance variables:
intptr_t* _sp; // stack pointer (from Thread::last_Java_sp) , java 的stack 指针
address _pc; // program counter (the next instruction after the call) 下一个指令的指针

CodeBlob* _cb; // CodeBlob that "owns" pc
enum deopt_state {
not_deoptimized,
is_deoptimized,
unknown
};

deopt_state _deopt_state;

...

};

bytecode

enum TosState {         // describes the tos cache contents
btos = 0, // byte, bool tos cached
ztos = 1, // byte, bool tos cached
ctos = 2, // char tos cached
stos = 3, // short tos cached
itos = 4, // int tos cached
ltos = 5, // long tos cached
ftos = 6, // float tos cached
dtos = 7, // double tos cached
atos = 8, // object cached
vtos = 9, // tos not cached
number_of_states,
ilgl // illegal state: should not occur
};

iload

bytecodeenumasm
iload21
$65 = (address) 0x7fffe1012693 "A\017\266]\002\203\373\025\017\204J"
(gdb) x/20i 0x7fffe1012693
0x7fffe1012693: movzbl 0x2(%r13),%ebx
0x7fffe1012698: cmp $0x15,%ebx <--- 下一个bytecode
0x7fffe101269b: je 0x7fffe10126eb <-- 跳转到 done
0x7fffe10126a1: cmp $0xe0,%ebx <-- 判断下一个是否是_fast_iload
0x7fffe10126a7: mov $0xe1,%ecx <------ 下一个是_fast_iload 则重写成fast_iload2
0x7fffe10126ac: je 0x7fffe10126bd <-------- 跳转到rewrite label
0x7fffe10126ae: cmp $0x34,%ebx
0x7fffe10126b1: mov $0xe2,%ecx
0x7fffe10126b6: je 0x7fffe10126bd
0x7fffe10126b8: mov $0xe0,%ecx
0x7fffe10126bd: movzbl 0x0(%r13),%ebx
0x7fffe10126c2: cmp $0x15,%ebx
0x7fffe10126c5: je 0x7fffe10126e7
0x7fffe10126cb: cmp %ecx,%ebx
0x7fffe10126cd: je 0x7fffe10126e7
0x7fffe10126d3: movabs $0x7ffff74ef9d7,%rdi
0x7fffe10126dd: and $0xfffffffffffffff0,%rsp
0x7fffe10126e1: call 0x7ffff694f3c0 <_ZN14MacroAssembler7debug64EPclPl>
0x7fffe10126e6: hlt
0x7fffe10126e7: mov %cl,0x0(%r13)

源码分析

void TemplateTable::iload_internal(RewriteControl rc) {
transition(vtos, itos);
if (RewriteFrequentPairs && rc == may_rewrite) {
Label rewrite, done;
Register bc = r4;

// get next bytecode
__ load_unsigned_byte(r1, at_bcp(Bytecodes::length_for(Bytecodes::_iload)));

// if _iload, wait to rewrite to iload2. We only want to rewrite the
// last two iloads in a pair. Comparing against fast_iload means that
// the next bytecode is neither an iload or a caload, and therefore
// an iload pair.
__ cmpw(r1, Bytecodes::_iload); <--- 下一个bytecode
__ br(Assembler::EQ, done); <---- 跳转到done

// if _fast_iload rewrite to _fast_iload2
__ cmpw(r1, Bytecodes::_fast_iload); <-- 判断下一个是否是_fast_iload
__ movw(bc, Bytecodes::_fast_iload2); <------ 下一个是_fast_iload 则重写成fast_iload2
__ br(Assembler::EQ, rewrite); <-------- 跳转到rewrite label

// if _caload rewrite to _fast_icaload
__ cmpw(r1, Bytecodes::_caload);
__ movw(bc, Bytecodes::_fast_icaload);
__ br(Assembler::EQ, rewrite);

// else rewrite to _fast_iload
__ movw(bc, Bytecodes::_fast_iload);

// rewrite
// bc: new bytecode
__ bind(rewrite);
patch_bytecode(Bytecodes::_iload, bc, r1, false);
__ bind(done);

}

// do iload, get the local value into tos
locals_index(r1);
__ ldr(r0, iaddress(r1));

}

aconst_null

bytecodedescenum
aconst_nullpush a null reference onto the stack0x01
void TemplateTable::aconst_null() {
transition(vtos, atos);
__ xorl(rax, rax); // rax 就是栈顶
}

istore

bytecodedescenum
istoreStore int into local variable54, // 0x36

可以通过这个bytecode 了解怎么访问本地变量

void TemplateTable::istore() {
transition(itos, vtos); // 这里只是一个断言assert , 断言之前的状态是itos , 之后的状态是vtos , 实际上是由def来定义的
locals_index(rbx); // 将偏移 也就是index 写入rbx
__ movl(iaddress(rbx), rax); //iaddress 就是 rlocal + rbx 也就是获取最后的跳转地址 ,然后将rax写入偏移地址
}

这里iaddress(rbx) 其实是rlocals+rbx 的偏移,也就是相对于本地变量的偏移

static inline Address iaddress(Register r) {
return Address(rlocals, r, Address::times_ptr);
}

iaddress 的源码在这里: src\hotspot\cpu\x86\assembler_x86.hpp 调用顺序是iaddress -> Address

static inline Address iaddress(Register r) {
return Address(rlocals, r, Address::times_ptr);
}
Address(Register base, Register index, ScaleFactor scale, int disp = 0)
: _base (base),
_index(index),
_xmmindex(xnoreg),
_scale(scale),
_disp (disp),
_isxmmindex(false) {
assert(!index->is_valid() == (scale == Address::no_scale),
"inconsistent address");
}

def istore展开

前面不是看到transition(itos, vtos); , 这个transition只是一个类似测试时候的断言,真正是在def 处理的

  def(Bytecodes::_istore              , ubcp|____|clvm|____, itos, vtos, istore              ,  _           );

下面我们看看def展开,会慢慢展开成

void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg), int arg) {
...
Template* t = is_wide ? template_for_wide(code) : template_for(code);
// setup entry
t->initialize(flags, in, out, gen, arg);
assert(t->bytecode() == code, "just checkin'");
}

这里的 in 和out 会在TemplateInterpreterGenerator::generate_and_dispatch的时候使用


//------------------------------------------------------------------------------------------------------------------------

void TemplateInterpreterGenerator::generate_and_dispatch(Template* t, TosState tos_out) {
#ifndef PRODUCT
// debugging code
if (CountBytecodes || TraceBytecodes || StopInterpreterAt > 0) count_bytecode();
if (PrintBytecodeHistogram) histogram_bytecode(t);
if (PrintBytecodePairHistogram) histogram_bytecode_pair(t);
if (TraceBytecodes) trace_bytecode(t);
if (StopInterpreterAt > 0) stop_interpreter_at();
__ verify_FPU(1, t->tos_in());
#endif // !PRODUCT
int step = 0;
if (!t->does_dispatch()) {
step = t->is_wide() ? Bytecodes::wide_length_for(t->bytecode()) : Bytecodes::length_for(t->bytecode());
if (tos_out == ilgl) tos_out = t->tos_out();
// compute bytecode size
assert(step > 0, "just checkin'");
// setup stuff for dispatching next bytecode
if (ProfileInterpreter && VerifyDataPointer
&& MethodData::bytecode_has_profile(t->bytecode())) {
__ verify_method_data_pointer();
}
__ dispatch_prolog(tos_out, step);
}
// generate template
t->generate(_masm);
// advance
if (t->does_dispatch()) {
#ifdef ASSERT
// make sure execution doesn't go beyond this point if code is broken
__ should_not_reach_here();
#endif // ASSERT
} else {
// dispatch to next bytecode
__ dispatch_epilog(tos_out, step);
}
}

reference

utf8 and utf16 and encoding and java

· One min read

背景

java 的字符串会设计很多编码相关的问题,全部整理一下

知识点

Code Unit

code unit 描述的是一个编码的的最小单位(注意一个Unicode 平面对应的字符可能由多个code unit 组成)

编码unit code
utf-81字节
utf-162字节

java 的char

java 的char 是2个字节,类型的范围是 0 到 2^16 - 1.

clickhouse mybatis batch insert cpu raise up

· 2 min read

背景

背景

使用jdbc clickhouse,批量写入,发现cpu升高非常多,升到了90%多

排查及原因

相关环境

jdk: jdk11 clickhouse 使用的sql驱动:

               <groupId>com.clickhouse</groupId>
<artifactId>clickhouse-jdbc</artifactId>
<version>0.3.2-patch11</version>

原因: 使用mybatis plus 拼写sql,批量写入2000条 sql 用mybatis 的xml 拼写 类似:

insert into table values (row1_field1 , row1_field2 ),(row1_field1 , row1_field2) .... 这里是用mybatis 的xml foreache 2000 次

线上warning日志:

Please consider to use one and only one values expression, for example: use 'values(?)' instead of 'values(?),(?)'.

由于jdbc 的parser 比较慢,需要将perpare语句改成以下形式:

insert into table values ( ?, ? )  ## 只有一次

采用的是

// Note: "insert into table values(?,?)" is treated as "insert into mytable"
try (PreparedStatement ps = conn.prepareStatement("insert into table values(?,?)")) {
ps.setString(1, "test"); // id
ps.setObject(2, LocalDateTime.now()); // timestamp
ps.addBatch(); // append parameters to the query
...
ps.executeBatch(); // issue the composed query: insert into mytable values(...)(...)...(...)
}

结果

cpu 从80%降低到30%以内 优化前:

优化前

优化后: 优化后

相关阅读

java 常用命令

· One min read
命令描述
java -XshowSettings:all -version获取所有配置

Garbage-First Garbage Collection 简单概况

· 8 min read

Garbage-First Garbage Collection

Garbage-First简称g1算法,是java目前可选的一个gc算法。

目标: 在一定的软实时性条件下,保证整体的吞吐

算法构成:

  • 堆等大小: 整个内存堆被划分为相同大小的块。

The Garbage-First collector achieves these goals via sev- eral techniques. The heap is partitioned into a set of equal- sized heap regions, much like the train cars of the Mature- Object Space collector of Hudson and Moss [22]. However, whereas the remembered sets of the Mature-Object Space collector are unidirectional, recording pointers from older regions to younger but not vice versa, Garbage-First remem- bered sets record pointers from all regions (with some excep- tions, described in sections 2.4 and 4.6). Recording all ref- erences allows an arbitrary set of heap regions to be chosen for collection. A concurrent thread processes log records cre- ated by special mutator write barriers to keep remembered sets up-to-date, allowing shorter collections.

源码分析

  • Young gc
void G1YoungCollector::collect() {
// Do timing/tracing/statistics/pre- and post-logging/verification work not
// directly related to the collection. They should not be accounted for in
// collection work timing.

// The G1YoungGCTraceTime message depends on collector state, so must come after
// determining collector state.
G1YoungGCTraceTime tm(this, _gc_cause);

// JFR
G1YoungGCJFRTracerMark jtm(gc_timer_stw(), gc_tracer_stw(), _gc_cause);
// JStat/MXBeans
G1MonitoringScope ms(monitoring_support(),
false /* full_gc */,
collector_state()->in_mixed_phase() /* all_memory_pools_affected */);
// Create the heap printer before internal pause timing to have
// heap information printed as last part of detailed GC log.
G1HeapPrinterMark hpm(_g1h);
// Young GC internal pause timing
G1YoungGCNotifyPauseMark npm(this);

// Verification may use the workers, so they must be set up before.
// Individual parallel phases may override this.
set_young_collection_default_active_worker_threads();

// Wait for root region scan here to make sure that it is done before any
// use of the STW workers to maximize cpu use (i.e. all cores are available
// just to do that).
wait_for_root_region_scanning();

G1YoungGCVerifierMark vm(this);
{
// Actual collection work starts and is executed (only) in this scope.

// Young GC internal collection timing. The elapsed time recorded in the
// policy for the collection deliberately elides verification (and some
// other trivial setup above).
policy()->record_young_collection_start();

calculate_collection_set(jtm.evacuation_info(), _target_pause_time_ms);

G1RedirtyCardsQueueSet rdcqs(G1BarrierSet::dirty_card_queue_set().allocator());
G1PreservedMarksSet preserved_marks_set(workers()->active_workers());
G1ParScanThreadStateSet per_thread_states(_g1h,
&rdcqs,
&preserved_marks_set,
workers()->active_workers(),
collection_set()->young_region_length(),
collection_set()->optional_region_length(),
&_evac_failure_regions);

pre_evacuate_collection_set(jtm.evacuation_info(), &per_thread_states);

bool may_do_optional_evacuation = collection_set()->optional_region_length() != 0;
// Actually do the work...
evacuate_initial_collection_set(&per_thread_states, may_do_optional_evacuation);

if (may_do_optional_evacuation) {
evacuate_optional_collection_set(&per_thread_states);
}
post_evacuate_collection_set(jtm.evacuation_info(), &per_thread_states);

// Refine the type of a concurrent mark operation now that we did the
// evacuation, eventually aborting it.
_concurrent_operation_is_full_mark = policy()->concurrent_operation_is_full_mark("Revise IHOP");

// Need to report the collection pause now since record_collection_pause_end()
// modifies it to the next state.
jtm.report_pause_type(collector_state()->young_gc_pause_type(_concurrent_operation_is_full_mark));

policy()->record_young_collection_end(_concurrent_operation_is_full_mark, evacuation_failed());
}
TASKQUEUE_STATS_ONLY(print_taskqueue_stats());
TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());
}

真正复制的代码

evacuate_live_objects(


MAYBE_INLINE_EVACUATION
void G1ParScanThreadState::dispatch_task(ScannerTask task) {
verify_task(task);
if (task.is_narrow_oop_ptr()) {
do_oop_evac(task.to_narrow_oop_ptr());
} else if (task.is_oop_ptr()) { //oop 复制
do_oop_evac(task.to_oop_ptr());
} else {
do_partial_array(task.to_partial_array_task());
}
}

堆栈:

Thread 23 "GC Thread#4" hit Breakpoint 1, G1ParScanThreadState::do_copy_to_survivor_space (this=0x7fff7c000d90, region_attr=..., old=0x716809d28, old_mark=...) at /home/dai/jdk/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:443
443 assert(region_attr.is_in_cset(),
(gdb) bt
#0 G1ParScanThreadState::do_copy_to_survivor_space (this=0x7fff7c000d90, region_attr=..., old=0x716809d28, old_mark=...) at /home/dai/jdk/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:443
#1 0x00007ffff64ab3f6 in G1ParScanThreadState::copy_to_survivor_space (this=0x7fff7c000d90, region_attr=..., old=0x716809d28, old_mark=...)
at /home/dai/jdk/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp:555
#2 0x00007ffff64de15e in G1ParCopyClosure<(G1Barrier)0, false>::do_oop_work<oopDesc*> (this=0x7fff7c001478, p=0x7ffff02e1cc8) at /home/dai/jdk/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp:240
#3 0x00007ffff64dcbc3 in G1ParCopyClosure<(G1Barrier)0, false>::do_oop (this=0x7fff7c001478, p=0x7ffff02e1cc8) at /home/dai/jdk/src/hotspot/share/gc/g1/g1OopClosures.hpp:167
#4 0x00007ffff6546dd8 in chunk_oops_do (f=0x7fff7c001478, chunk=0x7ffff02e1cb0, chunk_top=0x7ffff02e1cd0 "\350\034\200\026\a") at /home/dai/jdk/src/hotspot/share/runtime/handles.cpp:100
#5 0x00007ffff6546e23 in HandleArea::oops_do (this=0x7ffff02e1c30, f=0x7fff7c001478) at /home/dai/jdk/src/hotspot/share/runtime/handles.cpp:108
#6 0x00007ffff6d85dd4 in Thread::oops_do_no_frames (this=0x7ffff02e1160, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:550
#7 0x00007ffff6d8a513 in JavaThread::oops_do_no_frames (this=0x7ffff02e1160, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:1968
#8 0x00007ffff6d85e28 in Thread::oops_do (this=0x7ffff02e1160, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:580
#9 0x00007ffff6d91359 in ParallelOopsDoThreadClosure::do_thread (this=0x7fff87dfaa00, t=0x7ffff02e1160) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:3620
#10 0x00007ffff6d8c40b in Threads::possibly_parallel_threads_do (is_par=true, tc=0x7fff87dfaa00) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:2545
#11 0x00007ffff6d8eac8 in Threads::possibly_parallel_oops_do (is_par=true, f=0x7fff7c001478, cf=0x7fff7c001520) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:3626
#12 0x00007ffff64dea53 in G1RootProcessor::process_java_roots (this=0x7fffc9723df0, closures=0x7fff7c001470, phase_times=0x7fffb8001380, worker_id=1)
at /home/dai/jdk/src/hotspot/share/gc/g1/g1RootProcessor.cpp:183
#13 0x00007ffff64de78e in G1RootProcessor::evacuate_roots (this=0x7fffc9723df0, pss=0x7fff7c000d90, worker_id=1) at /home/dai/jdk/src/hotspot/share/gc/g1/g1RootProcessor.cpp:60
#14 0x00007ffff64f06b8 in G1EvacuateRegionsTask::scan_roots (this=0x7fffc9723f50, pss=0x7fff7c000d90, worker_id=1) at /home/dai/jdk/src/hotspot/share/gc/g1/g1YoungCollector.cpp:706
#15 0x00007ffff64f0632 in G1EvacuateRegionsBaseTask::work (this=0x7fffc9723f50, worker_id=1) at /home/dai/jdk/src/hotspot/share/gc/g1/g1YoungCollector.cpp:693
#16 0x00007ffff6e8bb7c in WorkerTaskDispatcher::worker_run_task (this=0x7ffff00a4c88) at /home/dai/jdk/src/hotspot/share/gc/shared/workerThread.cpp:67
#17 0x00007ffff6e8c074 in WorkerThread::run (this=0x7fffb800df30) at /home/dai/jdk/src/hotspot/share/gc/shared/workerThread.cpp:159
#18 0x00007ffff6d8557f in Thread::call_run (this=0x7fffb800df30) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:358
#19 0x00007ffff6acc1e7 in thread_native_entry (thread=0x7fffb800df30) at /home/dai/jdk/src/hotspot/os/linux/os_linux.cpp:705
#20 0x00007ffff7c94ac3 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#21 0x00007ffff7d26a40 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

内存复制:

static void pd_disjoint_words(const HeapWord* from, HeapWord* to, size_t count) {
#ifdef AMD64
switch (count) {
case 8: to[7] = from[7];
case 7: to[6] = from[6];
case 6: to[5] = from[5];
case 5: to[4] = from[4];
case 4: to[3] = from[3];
case 3: to[2] = from[2];
case 2: to[1] = from[1];
case 1: to[0] = from[0];
case 0: break;
default:
(void)memcpy(to, from, count * HeapWordSize);
break;
}
#else
// Includes a zero-count check.
intx temp;
__asm__ volatile(" testl %6,%6 ;"
" jz 3f ;"
" cmpl $32,%6 ;"
" ja 2f ;"
" subl %4,%1 ;"
"1: movl (%4),%3 ;"
" movl %7,(%5,%4,1);"
" addl $4,%0 ;"
" subl $1,%2 ;"
" jnz 1b ;"
" jmp 3f ;"
"2: rep; smovl ;"
"3: nop "
: "=S" (from), "=D" (to), "=c" (count), "=r" (temp)
: "0" (from), "1" (to), "2" (count), "3" (temp)
: "memory", "cc");
#endif // AMD64
}

并发标记: D:\jdk\src\hotspot\share\gc\g1\g1ConcurrentMark.inline.hpp

inline bool G1ConcurrentMark::mark_in_bitmap(uint const worker_id, oop const obj) {
HeapRegion* const hr = _g1h->heap_region_containing(obj);

if (hr->obj_allocated_since_marking_start(obj)) {
return false;
}

// Some callers may have stale objects to mark above TAMS after humongous reclaim.
// Can't assert that this is a valid object at this point, since it might be in the process of being copied by another thread.
assert(!hr->is_continues_humongous(), "Should not try to mark object " PTR_FORMAT " in Humongous continues region %u above TAMS " PTR_FORMAT, p2i(obj), hr->hrm_index(), p2i(hr->top_at_mark_start()));

bool success = _mark_bitmap.par_mark(obj);
if (success) {
add_to_liveness(worker_id, obj, obj->size());
}
return success;
}

分配内存:

HeapWord*
G1CollectedHeap::mem_allocate(size_t word_size,
bool* gc_overhead_limit_was_exceeded) {
assert_heap_not_locked_and_not_at_safepoint();

if (is_humongous(word_size)) {
return attempt_allocation_humongous(word_size);
}
size_t dummy = 0;
return attempt_allocation(word_size, word_size, &dummy);
}
  product(uint, GCCardSizeInBytes, 512,                                     \
"Card table entry size (in bytes) for card based collectors") \
range(128, NOT_LP64(512) LP64_ONLY(1024)) \
constraint(GCCardSizeInBytesConstraintFunc,AtParse)


cartable 为什么是512

  _card_size = GCCardSizeInBytes;
_card_shift = log2i_exact(_card_size);
_card_size_in_words = _card_size / sizeof(HeapWord);
template <DecoratorSet decorators, typename BarrierSetT>
template <typename T>
inline void G1BarrierSet::AccessBarrier<decorators, BarrierSetT>::
oop_store_not_in_heap(T* addr, oop new_value) {
// Apply SATB barriers for all non-heap references, to allow
// concurrent scanning of such references.
G1BarrierSet *bs = barrier_set_cast<G1BarrierSet>(BarrierSet::barrier_set());
bs->write_ref_field_pre<decorators>(addr);
Raw::oop_store(addr, new_value);
}

写入barrier逻辑

写入barrier逻辑

void G1BarrierSetAssembler::oop_store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
bool in_heap = (decorators & IN_HEAP) != 0;
bool as_normal = (decorators & AS_NORMAL) != 0;

bool needs_pre_barrier = as_normal;
bool needs_post_barrier = val != noreg && in_heap;

Register rthread = LP64_ONLY(r15_thread) NOT_LP64(rcx);
// flatten object address if needed
// We do it regardless of precise because we need the registers
if (dst.index() == noreg && dst.disp() == 0) {
if (dst.base() != tmp1) {
__ movptr(tmp1, dst.base());
}
} else {
__ lea(tmp1, dst);
}

#ifndef _LP64
InterpreterMacroAssembler *imasm = static_cast<InterpreterMacroAssembler*>(masm);
#endif

NOT_LP64(__ get_thread(rcx));
NOT_LP64(imasm->save_bcp());

if (needs_pre_barrier) {
g1_write_barrier_pre(masm /*masm*/,
tmp1 /* obj */,
tmp2 /* pre_val */,
rthread /* thread */,
tmp3 /* tmp */,
val != noreg /* tosca_live */,
false /* expand_call */);
}
if (val == noreg) {
BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg);
} else {
Register new_val = val;
if (needs_post_barrier) {
// G1 barrier needs uncompressed oop for region cross check.
if (UseCompressedOops) {
new_val = tmp2;
__ movptr(new_val, val);
}
}
BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg);
if (needs_post_barrier) {
g1_write_barrier_post(masm /*masm*/,
tmp1 /* store_adr */,
new_val /* new_val */,
rthread /* thread */,
tmp3 /* tmp */,
tmp2 /* tmp2 */);
}
}
NOT_LP64(imasm->restore_bcp());
}

hread 2 "java" hit Breakpoint 1, 0x00007ffff6fa66d8 in G1BarrierSetAssembler::oop_store_at(MacroAssembler*, unsigned long, BasicType, Address, Register, Register, Register, Register) ()
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
(gdb) bt
#0 0x00007ffff6fa66d8 in G1BarrierSetAssembler::oop_store_at(MacroAssembler*, unsigned long, BasicType, Address, Register, Register, Register, Register) ()
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#1 0x00007ffff7482012 in ModRefBarrierSetAssembler::store_at(MacroAssembler*, unsigned long, BasicType, Address, Register, Register, Register, Register) ()
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#2 0x00007ffff740992a in MacroAssembler::store_heap_oop(Address, Register, Register, Register, Register, unsigned long) ()
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#3 0x00007ffff77506ec in TemplateTable::aastore() ()
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#4 0x00007ffff7746b47 in Template::generate(InterpreterMacroAssembler*) ()
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#5 0x00007ffff7736a0c in TemplateInterpreterGenerator::generate_and_dispatch(Template*, TosState) ()
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#6 0x00007ffff77427f6 in TemplateInterpreterGenerator::set_vtos_entry_points(Template*, unsigned char*&, unsigned char*&, unsigned char*&, unsigned char*&, unsigned char*&, unsigned char*&, unsigned char*&, unsigned char*&, unsigned char*&) () from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#7 0x00007ffff77370d7 in TemplateInterpreterGenerator::set_entry_points(Bytecodes::Code) () from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#8 0x00007ffff773855b in TemplateInterpreterGenerator::generate_all() ()
--Type <RET> for more, q to quit, c to continue without paging--
from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#9 0x00007ffff7735d45 in TemplateInterpreter::initialize_code() () from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#10 0x00007ffff70f0007 in interpreter_init_code() () from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#11 0x00007ffff70cc117 in init_globals2() () from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#12 0x00007ffff7777839 in Threads::create_vm(JavaVMInitArgs*, bool*) () from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#13 0x00007ffff719c702 in JNI_CreateJavaVM () from /usr/lib/jvm/java-21-openjdk-amd64/lib/server/libjvm.so
#14 0x00007ffff7f81123 in JavaMain () from /usr/lib/jvm/java-21-openjdk-amd64/bin/../lib/libjli.so
#15 0x00007ffff7f84f0d in ThreadJavaMain () from /usr/lib/jvm/java-21-openjdk-amd64/bin/../lib/libjli.so
#16 0x00007ffff7e233ec in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444
#17 0x00007ffff7ea3a4c in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

相关阅读

java 一次gc排查

· 2 min read

背景

测试环境有个old generation 非常占用内存.所以需要排查

过程

问题出现

同事压测的时候,发现old generation 占用内存非常大 例子是下图的样子: java_gc_generation

相关配置:

  • jdk 11
  • 最大堆内存2g
  • gc算法用默认的g1

排查问题

dump 文件

使用jamp命令将java的内存dump出来

jmap -dump:format=b,,live,file=<file-path> <pid> 

mat工具分析

然后去下载mat工具下载地址 安装之后打开,点击Dominator Tree: Dominator Tree

就可以看到对象以及大小

Dominator Tree detail

可以看到有一个900m大小的HashSet , 这是一个去重的set , 每次都会往这里塞入设备id ,当循环结束,会自动不会再被引用,然后会被gc回收.

结论

这不是内存泄漏,而是一个有1kw 数据的的大set ,也就是一个大对象

我们找运维调大了最大的堆内存,问题解决

后续优化: 后续我们打算不使用HashSet 塞字符串去重,而是用布隆过滤器去重

java 对象大小

· 15 min read

背景

排查一个gc问题的时候想到需要了解java的对象大小

举例这样创建一个1099 的对象会占用多少个字节呢?

那如果是一个特定的对象NumClass

public class NumClass{
public int num1 ;
public int num2 ;
}

那样创建NunClass[] 又占用多少字节呢?

## 这样 的array占用多少字节呢? 
Object[] array = new Object[10245] ;
## 这样 的array占用多少字节呢?
Object[] array = new NumClass[10245] ;

结论是两者占用的字节是一样的

object 数组例子

先上最简单的new Object的代码,在这个例子里面: 创建了一个长度是10245的对象数组

package com;
public class Hello{
public static volatile Object[] arr ;
public static void main(String [] argv){
arr = new Object[10245]; // 创建一个对象数组 , 数组也是一个对象 , 那么这个对象有多大呢?
arr[1] = "hihi";

}
}

实际上是在64位机器上,没有开启指针压缩的情况下是: 8+ 4 + 10245*8个字节长度

oop 是指针oopDesc* 的别名

typedef class oopDesc*                    oop;
typedef class instanceOopDesc* instanceOop;
typedef class arrayOopDesc* arrayOop;
typedef class objArrayOopDesc* objArrayOop;
typedef class typeArrayOopDesc* typeArrayOop;
+-----------++----------++-------++------+           
| || || || |
| || || || |
| MarkWord || length || oop || oop |
+-----------++----------++-------++------+ repeat 10254 次

jol

核心函数:

jol\jol-core\src\main\java\org\openjdk\jol\layouters\CurrentLayouter.java

@Override
public ClassLayout layout(ClassData data) {
VirtualMachine vm = VM.current();

if (data.isArray()) {
// special case of arrays
int base = vm.arrayBaseOffset(data.arrayComponentType());
int scale = vm.arrayIndexScale(data.arrayComponentType());

long instanceSize = MathUtil.align(base + data.arrayLength() * scale, vm.objectAlignment());

SortedSet<FieldLayout> result = new TreeSet<>();
result.add(new FieldLayout(FieldData.create(data.arrayClass(), "<elements>", data.arrayComponentType()), base, scale * data.arrayLength()));
return ClassLayout.create(data, result, CURRENT, instanceSize, false);
}

Collection<FieldData> fields = data.fields();

SortedSet<FieldLayout> result = new TreeSet<>();
for (FieldData f : fields) {
result.add(new FieldLayout(f, vm.fieldOffset(f.refField()), vm.sizeOfField(f.typeClass())));
}

long instanceSize;
if (result.isEmpty()) {
instanceSize = vm.objectHeaderSize();
} else {
FieldLayout f = result.last();
instanceSize = f.offset() + f.size();
// TODO: This calculation is incorrect if there is a trailing @Contended field, or the instance is @Contended
}
instanceSize = MathUtil.align(instanceSize, vm.objectAlignment());
return ClassLayout.create(data, result, CURRENT, instanceSize, true);
}
  static int length_offset_in_bytes() {
return UseCompressedClassPointers ? klass_gap_offset_in_bytes() :
sizeof(arrayOopDesc);
}

创建一个数组

(gdb) bt
#0 arrayOopDesc::length_offset_in_bytes () at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:83
#1 0x00007f93c2fdc06e in arrayOopDesc::length_addr_impl (obj_ptr=0x7162010c0) at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:67
#2 0x00007f93c2fdc0ba in arrayOopDesc::length (this=0x7162010c0) at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:114
#3 0x00007f93c3239521 in arrayOopDesc::is_within_bounds (this=0x7162010c0, index=10) at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:110
#4 0x00007f93c3239690 in typeArrayOopDesc::byte_at_addr (this=0x7162010c0, which=10) at /var/jdk/src/hotspot/share/oops/typeArrayOop.inline.hpp:48
#5 0x00007f93c323972d in typeArrayOopDesc::byte_at_put (this=0x7162010c0, which=10, contents=112 'p') at /var/jdk/src/hotspot/share/oops/typeArrayOop.inline.hpp:96
#6 0x00007f93c389e259 in java_lang_String::create_from_unicode (unicode=0x7f93bc037c88, length=33, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/javaClasses.cpp:300
#7 0x00007f93c3f7b91d in StringTable::do_intern (string_or_null_h=..., name=0x7f93bc037c88, len=33, hash=2694772907, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/stringTable.cpp:347
#8 0x00007f93c3f7b87e in StringTable::intern (string_or_null_h=..., name=0x7f93bc037c88, len=33, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/stringTable.cpp:336
#9 0x00007f93c3f7b5b0 in StringTable::intern (symbol=0x7f93c0018d38, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/stringTable.cpp:296
#10 0x00007f93c35740b5 in ConstantPool::uncached_string_at (this=0x7f938c02e0a0, which=250, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/oops/constantPool.cpp:1171
#11 0x00007f93c36a19cd in fieldDescriptor::string_initial_value (this=0x7f93c2bd3a68, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/runtime/fieldDescriptor.cpp:103
#12 0x00007f93c389ff2d in initialize_static_string_field (fd=0x7f93c2bd3a68, mirror=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/javaClasses.cpp:809
#13 0x00007f93c38a0444 in initialize_static_field (fd=0x7f93c2bd3a68, mirror=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/javaClasses.cpp:866
#14 0x00007f93c3869f51 in InstanceKlass::do_local_static_fields (this=0x8000431a0, f=0x7f93c38a0365 <initialize_static_field(fieldDescriptor*, Handle, JavaThread*)>, mirror=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/oops/instanceKlass.cpp:1672
#15 0x00007f93c38a08d5 in java_lang_Class::initialize_mirror_fields (k=0x8000431a0, mirror=..., protection_domain=..., classData=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/javaClasses.cpp:930
#16 0x00007f93c38a10d1 in java_lang_Class::create_mirror (k=0x8000431a0, class_loader=..., module=..., protection_domain=..., classData=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/javaClasses.cpp:1035
#17 0x00007f93c34c6144 in ClassFileParser::fill_instance_klass (this=0x7f93c2bd3dd0, ik=0x8000431a0, changed_by_loadhook=false, cl_inst_info=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/classFileParser.cpp:5426
#18 0x00007f93c34c532e in ClassFileParser::create_instance_klass (this=0x7f93c2bd3dd0, changed_by_loadhook=false, cl_inst_info=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/classFileParser.cpp:5255
#19 0x00007f93c3b554d1 in KlassFactory::create_from_stream (stream=0x7f93bc036fb0, name=0x7f93c00001f8, loader_data=0x7f93bc121170, cl_info=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/klassFactory.cpp:202
#20 0x00007f93c34d86e9 in ClassLoader::load_class (name=0x7f93c00001f8, search_append_only=false, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/classLoader.cpp:1231
#21 0x00007f93c4000806 in SystemDictionary::load_instance_class_impl (class_name=0x7f93c00001f8, class_loader=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.cpp:1289
#22 0x00007f93c4000ba3 in SystemDictionary::load_instance_class (name_hash=2036240099, name=0x7f93c00001f8, class_loader=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.cpp:1354
#23 0x00007f93c3ffeca9 in SystemDictionary::resolve_instance_class_or_null (name=0x7f93c00001f8, class_loader=..., protection_domain=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.cpp:723
#24 0x00007f93c3ffd82e in SystemDictionary::resolve_instance_class_or_null_helper (class_name=0x7f93c00001f8, class_loader=..., protection_domain=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.cpp:294
#25 0x00007f93c3ffd6d4 in SystemDictionary::resolve_or_null (class_name=0x7f93c00001f8, class_loader=..., protection_domain=..., __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.cpp:277
#26 0x00007f93c3ffd617 in SystemDictionary::resolve_or_fail (class_name=0x7f93c00001f8, class_loader=..., protection_domain=..., throw_error=true, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.cpp:263
#27 0x00007f93c32b8d98 in SystemDictionary::resolve_or_fail (class_name=0x7f93c00001f8, throw_error=true, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.hpp:100
#28 0x00007f93c40dca98 in vmClasses::resolve (id=vmClassID::Throwable_klass_knum, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/vmClasses.cpp:99
#29 0x00007f93c40dcb96 in vmClasses::resolve_until (limit_id=vmClassID::SoftReference_klass_knum, start_id=@0x7f93c2bd48f0: vmClassID::Cloneable_klass_knum, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/vmClasses.cpp:108
#30 0x00007f93c40dd59a in vmClasses::resolve_through (last_id=vmClassID::Reference_klass_knum, start_id=@0x7f93c2bd48f0: vmClassID::Cloneable_klass_knum, __the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/vmClasses.hpp:64
#31 0x00007f93c40dce23 in vmClasses::resolve_all (__the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/vmClasses.cpp:168
#32 0x00007f93c4001ab2 in SystemDictionary::initialize (__the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/classfile/systemDictionary.cpp:1655
#33 0x00007f93c40812fb in Universe::genesis (__the_thread__=0x7f93bc028fa0) at /var/jdk/src/hotspot/share/memory/universe.cpp:335
#34 0x00007f93c408378f in universe2_init () at /var/jdk/src/hotspot/share/memory/universe.cpp:937
#35 0x00007f93c3863d8a in init_globals () at /var/jdk/src/hotspot/share/runtime/init.cpp:132
#36 0x00007f93c404a1ca in Threads::create_vm (args=0x7f93c2bd4d50, canTryAgain=0x7f93c2bd4c5b) at /var/jdk/src/hotspot/share/runtime/thread.cpp:2843
#37 0x00007f93c396f43b in JNI_CreateJavaVM_inner (vm=0x7f93c2bd4da8, penv=0x7f93c2bd4db0, args=0x7f93c2bd4d50) at /var/jdk/src/hotspot/share/prims/jni.cpp:3613
#38 0x00007f93c396f787 in JNI_CreateJavaVM (vm=0x7f93c2bd4da8, penv=0x7f93c2bd4db0, args=0x7f93c2bd4d50) at /var/jdk/src/hotspot/share/prims/jni.cpp:3701
#39 0x00007f93c50e6a6a in InitializeJVM (pvm=0x7f93c2bd4da8, penv=0x7f93c2bd4db0, ifn=0x7f93c2bd4e00) at /var/jdk/src/java.base/share/native/libjli/java.c:1459
#40 0x00007f93c50e35ec in JavaMain (_args=0x7ffedd44e1a0) at /var/jdk/src/java.base/share/native/libjli/java.c:411
#41 0x00007f93c50ea5ec in ThreadJavaMain (args=0x7ffedd44e1a0) at /var/jdk/src/java.base/unix/native/libjli/java_md.c:651
#42 0x00007f93c4f45b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#43 0x00007f93c4fd6bb4 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:100

分配数组大小:

  static int array_size(int length) {
const uint OopsPerHeapWord = HeapWordSize/heapOopSize;
assert(OopsPerHeapWord >= 1 && (HeapWordSize % heapOopSize == 0),
"Else the following (new) computation would be in error");
uint res = ((uint)length + OopsPerHeapWord - 1)/OopsPerHeapWord;
#ifdef ASSERT
// The old code is left in for sanity-checking; it'll
// go away pretty soon. XXX
// Without UseCompressedOops, this is simply:
// oop->length() * HeapWordsPerOop;
// With narrowOops, HeapWordsPerOop is 1/2 or equal 0 as an integer.
// The oop elements are aligned up to wordSize
const uint HeapWordsPerOop = heapOopSize/HeapWordSize;
uint old_res;
if (HeapWordsPerOop > 0) {
old_res = length * HeapWordsPerOop;
} else {
old_res = align_up((uint)length, OopsPerHeapWord)/OopsPerHeapWord;
}
assert(res == old_res, "Inconsistency between old and new.");
#endif // ASSERT
return res;
}
Thread 2 "java" hit Breakpoint 5, jni_invoke_static (env=0x7fca48029310, result=0x7fca4c534bf0, receiver=0x0, call_type=JNI_STATIC, method_id=0x7fca48542d50, args=0x7fca4c534c60, __the_thread__=0x7fca48029030) at /var/jdk/src/hotspot/share/prims/jni.cpp:881
881 args->push_arguments_on(&java_args);
(gdb) p method._value->print()
{method}
- this oop: 0x00007fca14411240
- method holder: 'com/Hello'
- constants: 0x00007fca14411020 constant pool [20] {0x00007fca14411020} for 'com/Hello' cache=0x00007fca14411348
- access: 0x9 public static
- name: 'main'
- signature: '([Ljava/lang/String;)V'
- max stack: 2
- max locals: 1
- size of params: 1
- method size: 13
- vtable index: -2
- i2i entry: 0x00007fca3900dc00
- adapters: AHE@0x00007fca4812b8d0: 0xb i2c: 0x00007fca39114d60 c2i: 0x00007fca39114e1a c2iUV: 0x00007fca39114de4 c2iNCI: 0x00007fca39114e57
- compiled entry 0x00007fca39114e1a
- code size: 10
- code start: 0x00007fca14411230
- code end (excl): 0x00007fca1441123a
- checked ex length: 0
- linenumber start: 0x00007fca1441123a
- localvar length: 0
$17 = void
(gdb) bt
#0 jni_invoke_static (env=0x7fca48029310, result=0x7fca4c534bf0, receiver=0x0, call_type=JNI_STATIC, method_id=0x7fca48542d50, args=0x7fca4c534c60, __the_thread__=0x7fca48029030) at /var/jdk/src/hotspot/share/prims/jni.cpp:881
#1 0x00007fca4d2c141c in jni_CallStaticVoidMethod (env=0x7fca48029310, cls=0x7fca4802b368, methodID=0x7fca48542d50) at /var/jdk/src/hotspot/share/prims/jni.cpp:1710
#2 0x00007fca4ea4415e in JavaMain (_args=0x7fff1ad56b60) at /var/jdk/src/java.base/share/native/libjli/java.c:545
#3 0x00007fca4ea4a5ec in ThreadJavaMain (args=0x7fff1ad56b60) at /var/jdk/src/java.base/unix/native/libjli/java_md.c:651
#4 0x00007fca4e8a5b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#5 0x00007fca4e936bb4 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:100
Thread 2 "java" hit Breakpoint 5, jni_invoke_static (env=0x7f8674029310, result=0x7f86791b0bf0, receiver=0x0, call_type=JNI_STATIC, method_id=0x7f8674652560, args=0x7f86791b0c60, __the_thread__=0x7f8674029030) at /var/jdk/src/hotspot/share/prims/jni.cpp:881
881 args->push_arguments_on(&java_args);
(gdb) p method._value->print()
{method}
- this oop: 0x00007f8644411240
- method holder: 'com/Hello'
- constants: 0x00007f8644411020 constant pool [20] {0x00007f8644411020} for 'com/Hello' cache=0x00007f8644411348
- access: 0x9 public static
- name: 'main'
- signature: '([Ljava/lang/String;)V'
- max stack: 2
- max locals: 1
- size of params: 1
- method size: 13
- vtable index: -2
- i2i entry: 0x00007f866500dc00
- adapters: AHE@0x00007f867412b8d0: 0xb i2c: 0x00007f8665114d60 c2i: 0x00007f8665114e1a c2iUV: 0x00007f8665114de4 c2iNCI: 0x00007f8665114e57
- compiled entry 0x00007f8665114e1a
- code size: 10
- code start: 0x00007f8644411230
- code end (excl): 0x00007f864441123a
- checked ex length: 0
- linenumber start: 0x00007f864441123a
- localvar length: 0
$23 = void
(gdb) enable 1
(gdb) c
Continuing.

Thread 2 "java" hit Breakpoint 1, oopFactory::new_objArray (klass=0x800040f80, length=2019, __the_thread__=0x7f8674029030) at /var/jdk/src/hotspot/share/memory/oopFactory.cpp:118
118 assert(klass->is_klass(), "must be instance class");
(gdb) bt
#0 oopFactory::new_objArray (klass=0x800040f80, length=2019, __the_thread__=0x7f8674029030) at /var/jdk/src/hotspot/share/memory/oopFactory.cpp:118
#1 0x00007f8679e68a5b in InterpreterRuntime::anewarray (current=0x7f8674029030, pool=0x7f8644411020, index=2, size=2019) at /var/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:254
#2 0x00007f8665024083 in ?? ()
#3 0x00007f867b4520a0 in TemplateInterpreter::_active_table () from /var/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#4 0x00007f8665024002 in ?? ()
#5 0x00007f86791b07b0 in ?? ()
#6 0x00007f8644411233 in ?? ()
#7 0x00007f86791b0808 in ?? ()
#8 0x00007f8644411348 in ?? ()
#9 0x0000000000000000 in ?? ()
Bottom (innermost) frame selected; you cannot go down.
(gdb) p _do_zero
$35 = true
(gdb) n
413 if (_do_zero) {
(gdb) n
414 mem_clear(mem);
(gdb) n
416 arrayOopDesc::set_length(mem, _length);
(gdb) l
411 // concurrent GC.
412 assert(_length >= 0, "length should be non-negative");
413 if (_do_zero) {
414 mem_clear(mem);
415 }
416 arrayOopDesc::set_length(mem, _length);
417 return finish(mem);
418 }
419
420 oop ClassAllocator::initialize(HeapWord* mem) const {
(gdb) up
#1 0x00007f867a27af4b in MemAllocator::allocate (this=0x7f86791b0650) at /var/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:365
365 obj = initialize(mem);
(gdb) down
#0 ObjArrayAllocator::initialize (this=0x7f86791b0650, mem=0x715e73dd0) at /var/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:416
416 arrayOopDesc::set_length(mem, _length);
(gdb) s
arrayOopDesc::set_length (mem=0x715e73dd0, length=2019) at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:122
122 *length_addr_impl(mem) = length;
(gdb) s
arrayOopDesc::length_addr_impl (obj_ptr=0x715e73dd0) at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:66
66 char* ptr = static_cast<char*>(obj_ptr);
(gdb) l
61 return (int)hs;
62 }
63
64 // Returns the address of the length "field". See length_offset_in_bytes().
65 static int* length_addr_impl(void* obj_ptr) {
66 char* ptr = static_cast<char*>(obj_ptr);
67 return reinterpret_cast<int*>(ptr + length_offset_in_bytes());
68 }
69
70 // Check whether an element of a typeArrayOop with the given type must be
(gdb) n
67 return reinterpret_cast<int*>(ptr + length_offset_in_bytes());
(gdb) s
arrayOopDesc::length_offset_in_bytes () at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:83
83 sizeof(arrayOopDesc);
(gdb) l
78 // The _length field is not declared in C++. It is allocated after the
79 // declared nonstatic fields in arrayOopDesc if not compressed, otherwise
80 // it occupies the second half of the _klass field in oopDesc.
81 static int length_offset_in_bytes() {
82 return UseCompressedClassPointers ? klass_gap_offset_in_bytes() :
83 sizeof(arrayOopDesc);
84 }
85
86 // Returns the offset of the first element.
87 static int base_offset_in_bytes(BasicType type) {
(gdb) p Use
Display all 161 possibilities? (y or n)
(gdb) p UseCompressedClassPointers
$36 = true
(gdb) s
82 return UseCompressedClassPointers ? klass_gap_offset_in_bytes() :
(gdb) s
oopDesc::klass_gap_offset_in_bytes () at /var/jdk/src/hotspot/share/oops/oop.hpp:307
307 assert(has_klass_gap(), "only applicable to compressed klass pointers");
(gdb) l
302
303 // for code generation
304 static int mark_offset_in_bytes() { return offset_of(oopDesc, _mark); }
305 static int klass_offset_in_bytes() { return offset_of(oopDesc, _metadata._klass); }
306 static int klass_gap_offset_in_bytes() {
307 assert(has_klass_gap(), "only applicable to compressed klass pointers");
308 return klass_offset_in_bytes() + sizeof(narrowKlass);
309 }
310
311 // for error reporting
(gdb) n
oopDesc::klass_gap_offset_in_bytes () at /var/jdk/src/hotspot/share/oops/oop.hpp:308
308 return klass_offset_in_bytes() + sizeof(narrowKlass);
(gdb) n
309 }
(gdb) n
arrayOopDesc::length_offset_in_bytes () at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:83
83 sizeof(arrayOopDesc);
(gdb) n
84 }
(gdb) n
arrayOopDesc::length_addr_impl (obj_ptr=0x715e73dd0) at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:68
68 }
(gdb) n
arrayOopDesc::set_length (mem=0x715e73dd0, length=2019) at /var/jdk/src/hotspot/share/oops/arrayOop.hpp:122
122 *length_addr_impl(mem) = length;
(gdb) n
123 }
(gdb) p (int *)mem@20
Only values in memory can be extended with '@'.
(gdb) p *(int *)mem@20
$37 = {-1163019586, -1163019586, -1163019586, 2019, 0 <repeats 16 times>}

byte code 生成入口

// jdk/src/hotspot/share/interpreter/templateInterpreter.cpp
void DispatchTable::set_entry(int i, EntryPoint& entry) {
assert(0 <= i && i < length, "index out of bounds");
assert(number_of_states == 10, "check the code below");
_table[btos][i] = entry.entry(btos);
_table[ztos][i] = entry.entry(ztos);
_table[ctos][i] = entry.entry(ctos);
_table[stos][i] = entry.entry(stos);
_table[atos][i] = entry.entry(atos);
_table[itos][i] = entry.entry(itos);
_table[ltos][i] = entry.entry(ltos);
_table[ftos][i] = entry.entry(ftos);
_table[dtos][i] = entry.entry(dtos);
_table[vtos][i] = entry.entry(vtos);
}
//src/hotspot/share/interpreter/bytecodes.hpp
_new = 187, // 0xbb
_newarray = 188, // 0xbc
_anewarray = 189, // 0xbd
_arraylength = 190, // 0xbe

堆栈:

Thread 2 "java" hit Breakpoint 13, 0x00007fffe1011b13 in ?? ()
(gdb) x/20i $pc
=> 0x7fffe1011b13: movzwl 0x1(%r13),%eax
0x7fffe1011b18: bswap %eax
0x7fffe1011b1a: sar $0x10,%eax
0x7fffe1011b1d: movzbl 0x3(%r13),%ebx
0x7fffe1011b22: add $0x3,%r13
0x7fffe1011b26: movabs $0x7ffff7bca0a0,%r10
0x7fffe1011b30: jmp *(%r10,%rbx,8)
0x7fffe1011b34: nop
0x7fffe1011b35: nop
0x7fffe1011b36: nop
0x7fffe1011b37: nop
0x7fffe1011b38: int3
0x7fffe1011b39: int3
0x7fffe1011b3a: int3
0x7fffe1011b3b: int3
0x7fffe1011b3c: int3
0x7fffe1011b3d: int3
0x7fffe1011b3e: int3
0x7fffe1011b3f: int3
0x7fffe1011b40: and %al,(%rax,%rax,1)

0x00007fffe1011b1d in ?? ()
(gdb) info registers
rax 0x2805 10245
rbx 0x11 17
rcx 0x2 2
rdx 0x8 8
rsi 0x555555581230 93824992416304
rdi 0x7ffff0028f70 140737220087664
rbp 0x7ffff59fe7f8 0x7ffff59fe7f8
rsp 0x7ffff59fe7b0 0x7ffff59fe7b0
r8 0x8 8
r9 0x0 0
r10 0x7ffff7bcc8a0 140737349732512
r11 0x216 534
r12 0x0 0
r13 0x7fffb4411230 140736217551408
r14 0x7ffff59fe808 140737314285576
r15 0x7ffff0028f70 140737220087664
rip 0x7fffe1011b1d 0x7fffe1011b1d
eflags 0x216 [ PF AF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0

anewarray 汇编代码:

(gdb) x/20i $pc
=> 0x7fffe102400a: lea 0x8(%rsp),%rax
0x7fffe102400f: mov %r13,-0x40(%rbp)
0x7fffe1024013: cmpq $0x0,-0x10(%rbp)
0x7fffe102401b: je 0x7fffe1024035
0x7fffe1024021: movabs $0x7ffff71becc8,%rdi
0x7fffe102402b: and $0xfffffffffffffff0,%rsp
0x7fffe102402f: call 0x7ffff694f3c0 <_ZN14MacroAssembler7debug64EPclPl>
0x7fffe1024034: hlt
0x7fffe1024035: push %r10
0x7fffe1024037: cmp 0x16ae2ec2(%rip),%r12 # 0x7ffff7b06f00 <_ZN14CompressedOops11_narrow_oopE>
0x7fffe102403e: je 0x7fffe1024058
0x7fffe1024044: movabs $0x7ffff7311c28,%rdi
0x7fffe102404e: and $0xfffffffffffffff0,%rsp
0x7fffe1024052: call 0x7ffff694f3c0 <_ZN14MacroAssembler7debug64EPclPl>
0x7fffe1024057: hlt
0x7fffe1024058: pop %r10
0x7fffe102405a: mov %r15,%rdi
0x7fffe102405d: vzeroupper
0x7fffe1024060: mov %rbp,0x2d0(%r15)
0x7fffe1024067: mov %rax,0x2c0(%r15)
(gdb) x/200i $pc
=> 0x7fffe102400a: lea 0x8(%rsp),%rax
0x7fffe102400f: mov %r13,-0x40(%rbp)
0x7fffe1024013: cmpq $0x0,-0x10(%rbp)
0x7fffe102401b: je 0x7fffe1024035
0x7fffe1024021: movabs $0x7ffff71becc8,%rdi
0x7fffe102402b: and $0xfffffffffffffff0,%rsp
0x7fffe102402f: call 0x7ffff694f3c0 <_ZN14MacroAssembler7debug64EPclPl>
0x7fffe1024034: hlt
0x7fffe1024035: push %r10
0x7fffe1024037: cmp 0x16ae2ec2(%rip),%r12 # 0x7ffff7b06f00 <_ZN14CompressedOops11_narrow_oopE>
0x7fffe102403e: je 0x7fffe1024058
0x7fffe1024044: movabs $0x7ffff7311c28,%rdi
0x7fffe102404e: and $0xfffffffffffffff0,%rsp
0x7fffe1024052: call 0x7ffff694f3c0 <_ZN14MacroAssembler7debug64EPclPl>
0x7fffe1024057: hlt
0x7fffe1024058: pop %r10
0x7fffe102405a: mov %r15,%rdi
0x7fffe102405d: vzeroupper
0x7fffe1024060: mov %rbp,0x2d0(%r15)
0x7fffe1024067: mov %rax,0x2c0(%r15)
0x7fffe102406e: test $0xf,%esp
0x7fffe1024074: je 0x7fffe102408c
0x7fffe102407a: sub $0x8,%rsp
0x7fffe102407e: call 0x7ffff65cf968 <_ZN18InterpreterRuntime9anewarrayEP10JavaThreadP12ConstantPoolii>
0x7fffe1024083: add $0x8,%rsp
0x7fffe1024087: jmp 0x7fffe1024091
0x7fffe102408c: call 0x7ffff65cf968 <_ZN18InterpreterRuntime9anewarrayEP10JavaThreadP12ConstantPoolii>
0x7fffe1024091: push %rax
0x7fffe1024092: push %rdi
0x7fffe1024093: push %rsi
0x7fffe1024094: push %rdx
--Type <RET> for more, q to quit, c to continue without paging--
0x7fffe1024095: push %rcx
0x7fffe1024096: push %r8
0x7fffe1024098: push %r9
0x7fffe102409a: push %r10
0x7fffe102409c: push %r11
0x7fffe102409e: test $0xf,%esp
0x7fffe10240a4: je 0x7fffe10240bc
0x7fffe10240aa: sub $0x8,%rsp
0x7fffe10240ae: call 0x7ffff5d1c04e <_ZN6Thread7currentEv>
0x7fffe10240b3: add $0x8,%rsp
0x7fffe10240b7: jmp 0x7fffe10240c1
0x7fffe10240bc: call 0x7ffff5d1c04e <_ZN6Thread7currentEv>
0x7fffe10240c1: pop %r11
0x7fffe10240c3: pop %r10
0x7fffe10240c5: pop %r9
0x7fffe10240c7: pop %r8
0x7fffe10240c9: pop %rcx
0x7fffe10240ca: pop %rdx
0x7fffe10240cb: pop %rsi
0x7fffe10240cc: pop %rdi
0x7fffe10240cd: cmp %rax,%r15
0x7fffe10240d0: je 0x7fffe10240ea
0x7fffe10240d6: movabs $0x7ffff7311da0,%rdi
0x7fffe10240e0: and $0xfffffffffffffff0,%rsp
0x7fffe10240e4: call 0x7ffff694f3c0 <_ZN14MacroAssembler7debug64EPclPl>
0x7fffe10240e9: hlt
0x7fffe10240ea: pop %rax
0x7fffe10240eb: movq $0x0,0x2c0(%r15)
0x7fffe10240f6: movq $0x0,0x2d0(%r15)
0x7fffe1024101: movq $0x0,0x2c8(%r15)
0x7fffe102410c: vzeroupper
--Type <RET> for more, q to quit, c to continue without paging--
0x7fffe102410f: cmpq $0x0,0x8(%r15)
0x7fffe1024117: je 0x7fffe1024122
0x7fffe102411d: jmp 0x7fffe1000c20
0x7fffe1024122: mov 0x318(%r15),%rax
0x7fffe1024129: movq $0x0,0x318(%r15)
0x7fffe1024134: mov -0x40(%rbp),%r13
0x7fffe1024138: mov -0x38(%rbp),%r14
0x7fffe102413c: ret
0x7fffe102413d: movzbl 0x3(%r13),%ebx
0x7fffe1024142: add $0x3,%r13
0x7fffe1024146: movabs $0x7ffff7bcc0a0,%r10
0x7fffe1024150: jmp *(%r10,%rbx,8)
0x7fffe1024154: nop
0x7fffe1024155: nop


内存分配

用的是jdk11以上版本,我这个jdk是用g1来做gc的,所以看看g1 是怎么分配的

(gdb) bt
#0 HeapRegion::par_allocate_impl (this=0x7ffff00e11b0, min_word_size=256, desired_word_size=63020, actual_size=0x7ffff59fcba8)
at /home/dai/jdk/src/hotspot/share/gc/g1/heapRegion.inline.hpp:63
#1 0x00007ffff640bdcc in HeapRegion::par_allocate (this=0x7ffff00e11b0, min_word_size=256, desired_word_size=63020, actual_word_size=0x7ffff59fcba8)
at /home/dai/jdk/src/hotspot/share/gc/g1/heapRegion.inline.hpp:225
#2 0x00007ffff640bfcb in G1AllocRegion::par_allocate (this=0x7ffff0052e10, alloc_region=0x7ffff00e11b0, min_word_size=256, desired_word_size=63020,
actual_word_size=0x7ffff59fcba8) at /home/dai/jdk/src/hotspot/share/gc/g1/g1AllocRegion.inline.hpp:63
#3 0x00007ffff640c0c6 in G1AllocRegion::attempt_allocation (this=0x7ffff0052e10, min_word_size=256, desired_word_size=63020, actual_word_size=0x7ffff59fcba8)
at /home/dai/jdk/src/hotspot/share/gc/g1/g1AllocRegion.inline.hpp:77
#4 0x00007ffff6447142 in G1Allocator::attempt_allocation (this=0x7ffff0052d50, min_word_size=256, desired_word_size=63020, actual_word_size=0x7ffff59fcba8)
at /home/dai/jdk/src/hotspot/share/gc/g1/g1Allocator.inline.hpp:62
#5 0x00007ffff6447b1d in G1CollectedHeap::attempt_allocation (this=0x7ffff0048bf0, min_word_size=256, desired_word_size=63020,
actual_word_size=0x7ffff59fcba8) at /home/dai/jdk/src/hotspot/share/gc/g1/g1CollectedHeap.cpp:709
#6 0x00007ffff64385ea in G1CollectedHeap::allocate_new_tlab (this=0x7ffff0048bf0, min_size=256, requested_size=63020, actual_size=0x7ffff59fcba8)
at /home/dai/jdk/src/hotspot/share/gc/g1/g1CollectedHeap.cpp:359
#7 0x00007ffff69e1cf6 in MemAllocator::allocate_inside_tlab_slow (this=0x7ffff59fcc00, allocation=...)
at /home/dai/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:318
#8 0x00007ffff69e1bc2 in MemAllocator::allocate_inside_tlab (this=0x7ffff59fcc00, allocation=...)
at /home/dai/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:278
#9 0x00007ffff69e1eb9 in MemAllocator::mem_allocate (this=0x7ffff59fcc00, allocation=...) at /home/dai/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:350
#10 0x00007ffff69e1f22 in MemAllocator::allocate (this=0x7ffff59fcc00) at /home/dai/jdk/src/hotspot/share/gc/shared/memAllocator.cpp:363
#11 0x00007ffff6260d84 in CollectedHeap::array_allocate (this=0x7ffff0048bf0, klass=0x8000407c0, size=106, length=825, do_zero=true,
__the_thread__=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/share/gc/shared/collectedHeap.inline.hpp:41
#12 0x00007ffff6db9bf2 in TypeArrayKlass::allocate_common (this=0x8000407c0, length=825, do_zero=true, __the_thread__=0x7ffff0028f70)
at /home/dai/jdk/src/hotspot/share/oops/typeArrayKlass.cpp:93
#13 0x00007ffff62f7428 in TypeArrayKlass::allocate (this=0x8000407c0, length=825, __the_thread__=0x7ffff0028f70)
at /home/dai/jdk/src/hotspot/share/oops/typeArrayKlass.hpp:68
#14 0x00007ffff6ab4757 in oopFactory::new_typeArray (type=T_BYTE, length=825, __the_thread__=0x7ffff0028f70)
at /home/dai/jdk/src/hotspot/share/memory/oopFactory.cpp:93
#15 0x00007ffff65cf8e5 in InterpreterRuntime::newarray (current=0x7ffff0028f70, type=T_BYTE, size=825)
at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:247
#16 0x00007fffe1023eb2 in ?? ()
#17 0x00007ffff7bca0a0 in TemplateInterpreter::_active_table () from /home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#18 0x00007fffe1023e31 in ?? ()
#19 0x000000062a47ab38 in ?? ()
#20 0x00007ffff59fcd88 in ?? ()
#21 0x00007fffb43a23e6 in ?? ()
#22 0x00007ffff59fcde8 in ?? ()
#23 0x00007fffb43a3520 in ?? ()
#24 0x0000000000000000 in ?? ()

array_copy 反查

java里面 System.arraycopy 函数就是copy 整个数组到新的数组里面,复制方式是浅拷贝. 最后调用的入口是下面的c++代码.所以可以通过这个函数就可以了解数组的内存布局

void ObjArrayKlass::copy_array(arrayOop s, int src_pos, arrayOop d,
int dst_pos, int length, TRAPS) {


...
size_t src_offset = (size_t) objArrayOopDesc::obj_at_offset<oop>(src_pos); <----------------------------- 开始地址
size_t dst_offset = (size_t) objArrayOopDesc::obj_at_offset<oop>(dst_pos); <---------------------------- 结束地址
assert(arrayOopDesc::obj_offset_to_raw<oop>(s, src_offset, NULL) ==
objArrayOop(s)->obj_at_addr<oop>(src_pos), "sanity");
assert(arrayOopDesc::obj_offset_to_raw<oop>(d, dst_offset, NULL) ==
objArrayOop(d)->obj_at_addr<oop>(dst_pos), "sanity");
do_copy(s, src_offset, d, dst_offset, length, CHECK);
}
}

所以java的对象数组的内存布局就像下面一样 , 这里的oop 是一个指针

64位下面是8字节

相关阅读