Skip to main content

Payload value must not be empty

· 3 min read

背景

生产环境rabbitmq遇到错误:Payload value must not be empty

Caused by: org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException: Could not resolve method parameter at index 0 in public void com.xxxx.consume(java.lang.String,org.springframework.amqp.core.Message,com.rabbitmq.client.Channel) throws java.lang.Exception: 1 error(s): [Error in object 'content': codes []; arguments []; default message [Payload value must not be empty]] 
at org.springframework.messaging.handler.annotation.support.PayloadMethodArgumentResolver.resolveArgument(PayloadMethodArgumentResolver.java:122)
at org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:117)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:147)
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:115)
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:75)
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:261)

解决方式

添加注解@Payload

    @RabbitListener(queues = "queue")
public void consume( @Payload(required = false) String content, Message message, Channel channel) throws Exception { // 将@Payload 塞到 content 字段
...
}

查看源码

扒了一下代码查看原因: 路径

  • org\springframework\messaging\handler\annotation\support\PayloadMethodArgumentResolver.java
	public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
Payload ann = parameter.getParameterAnnotation(Payload.class);
if (ann != null && StringUtils.hasText(ann.expression())) {
throw new IllegalStateException("@Payload SpEL expressions not supported by this resolver");
}

Object payload = message.getPayload();
if (isEmptyPayload(payload)) { // 条件1
if (ann == null || ann.required()) { // 条件2
String paramName = getParameterName(parameter);
BindingResult bindingResult = new BeanPropertyBindingResult(payload, paramName);
bindingResult.addError(new ObjectError(paramName, "Payload value must not be empty")); // 在这里会校验空
throw new MethodArgumentNotValidException(message, parameter, bindingResult);
}
else {
return null;
}
}
}
  • 条件1 : payload 是空
  • 条件2 : 没有@Payload 注解或者 @Payload(required = ture)

原因: 满足条件1和条件2就会抛出异常

默认不加注解的时候会满足条件1和2 , 所以解决方案: 添加注解@Payload

    @RabbitListener(queues = "queue")
public void consume( @Payload(required = false) String content, Message message, Channel channel) throws Exception { // 将@Payload 塞到 content 字段
...
}

堆栈

resolveArgument:111, PayloadMethodArgumentResolver (org.springframework.messaging.handler.annotation.support)
resolveArgument:117, HandlerMethodArgumentResolverComposite (org.springframework.messaging.handler.invocation)
getMethodArgumentValues:147, InvocableHandlerMethod (org.springframework.messaging.handler.invocation)
invoke:115, InvocableHandlerMethod (org.springframework.messaging.handler.invocation)
invoke:75, HandlerAdapter (org.springframework.amqp.rabbit.listener.adapter)
invokeHandler:261, MessagingMessageListenerAdapter (org.springframework.amqp.rabbit.listener.adapter)
invokeHandlerAndProcessResult:207, MessagingMessageListenerAdapter (org.springframework.amqp.rabbit.listener.adapter)
onMessage:146, MessagingMessageListenerAdapter (org.springframework.amqp.rabbit.listener.adapter)
doInvokeListener:1665, AbstractMessageListenerContainer (org.springframework.amqp.rabbit.listener)
actualInvokeListener:1584, AbstractMessageListenerContainer (org.springframework.amqp.rabbit.listener)
invokeListener:-1, 1393414871 (org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer$$Lambda$1549)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:62, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:566, Method (java.lang.reflect)
invokeJoinpointUsingReflection:344, AopUtils (org.springframework.aop.support)
invokeJoinpoint:198, ReflectiveMethodInvocation (org.springframework.aop.framework)
proceed:163, ReflectiveMethodInvocation (org.springframework.aop.framework)
doWithRetry:93, RetryOperationsInterceptor$1 (org.springframework.retry.interceptor)
doExecute:329, RetryTemplate (org.springframework.retry.support)
execute:225, RetryTemplate (org.springframework.retry.support)
invoke:116, RetryOperationsInterceptor (org.springframework.retry.interceptor)
proceed:186, ReflectiveMethodInvocation (org.springframework.aop.framework)
invoke:215, JdkDynamicAopProxy (org.springframework.aop.framework)
invokeListener:-1, $Proxy247 (org.springframework.amqp.rabbit.listener)
invokeListener:1572, AbstractMessageListenerContainer (org.springframework.amqp.rabbit.listener)
doExecuteListener:1563, AbstractMessageListenerContainer (org.springframework.amqp.rabbit.listener)
executeListener:1507, AbstractMessageListenerContainer (org.springframework.amqp.rabbit.listener)
doReceiveAndExecute:967, SimpleMessageListenerContainer (org.springframework.amqp.rabbit.listener)
receiveAndExecute:914, SimpleMessageListenerContainer (org.springframework.amqp.rabbit.listener)
access$1600:83, SimpleMessageListenerContainer (org.springframework.amqp.rabbit.listener)
mainLoop:1291, SimpleMessageListenerContainer$AsyncMessageProcessingConsumer (org.springframework.amqp.rabbit.listener)
run:1197, SimpleMessageListenerContainer$AsyncMessageProcessingConsumer (org.springframework.amqp.rabbit.listener)
run:834, Thread (java.lang)

WFST 和lucene 和fst

· One min read

在WFST(Weighted Finite State Transducer,加权有限状态转换器)中,"All Pairs Shortest Path"(APSP)算法用于计算任意两个状态之间的最短路径。在WFST中,每个状态之间都有一条带有权重的边,表示从一个状态到另一个状态的转换。APSP算法的目标是找到连接任意两个状态的最短路径,即具有最小总权重的路径。

相关阅读

java 线程池

· One min read

背景

了解java源码包里面线程池的细节

线程池

线程池是管理一堆线程的对象。对于线程池来说,不同线程池主要是他们创建、调度、销毁的的各种策略的不一样

基础类

Executors

executor/executors/executorService:

executor 是一个接口,类似Runable , 实际上也是差不多功能

public interface Executor {
void execute(Runnable var1);
}

fst 结构

· 6 min read

背景

了解lucene 的fst结构

核心函数

freeezeTail -> compileNode

  private void freezeTail(int prefixLenPlus1) throws IOException {  // 入参是一个偏移值:  公共前缀+ 1
final int downTo = Math.max(1, prefixLenPlus1);
for (int idx = lastInput.length(); idx >= downTo; idx--) {

boolean doPrune = false;
boolean doCompile = false;


if (doCompile) {
...
parent.replaceLast(
lastInput.intAt(idx - 1),
compileNode(node, 1 + lastInput.length() - idx),
nextFinalOutput,
isFinal);
...
}
}
}
}
  private CompiledNode compileNode(UnCompiledNode<T> nodeIn, int tailLength) throws IOException {
final long node;
long bytesPosStart = bytes.getPosition();
if (dedupHash != null
&& (doShareNonSingletonNodes || nodeIn.numArcs <= 1)
&& tailLength <= shareMaxTailLength) {
if (nodeIn.numArcs == 0) {
node = fst.addNode(this, nodeIn);
lastFrozenNode = node;
} else {
node = dedupHash.add(this, nodeIn);
}
} else {
node = fst.addNode(this, nodeIn);
}
assert node != -2;

long bytesPosEnd = bytes.getPosition();
if (bytesPosEnd != bytesPosStart) {
// The FST added a new node:
assert bytesPosEnd > bytesPosStart;
lastFrozenNode = node;
}

nodeIn.clear();

final CompiledNode fn = new CompiledNode();
fn.node = node;
return fn;
}
  // serializes new node by appending its bytes to the end
// of the current byte[]
long addNode(FSTCompiler<T> fstCompiler, FSTCompiler.UnCompiledNode<T> nodeIn)
throws IOException {
T NO_OUTPUT = outputs.getNoOutput();

// System.out.println("FST.addNode pos=" + bytes.getPosition() + " numArcs=" + nodeIn.numArcs);
if (nodeIn.numArcs == 0) {
if (nodeIn.isFinal) {
return FINAL_END_NODE;
} else {
return NON_FINAL_END_NODE;
}
}
final long startAddress = fstCompiler.bytes.getPosition();
// System.out.println(" startAddr=" + startAddress);

final boolean doFixedLengthArcs = shouldExpandNodeWithFixedLengthArcs(fstCompiler, nodeIn);
if (doFixedLengthArcs) {
// System.out.println(" fixed length arcs");
if (fstCompiler.numBytesPerArc.length < nodeIn.numArcs) {
fstCompiler.numBytesPerArc = new int[ArrayUtil.oversize(nodeIn.numArcs, Integer.BYTES)];
fstCompiler.numLabelBytesPerArc = new int[fstCompiler.numBytesPerArc.length];
}
}

fstCompiler.arcCount += nodeIn.numArcs;

final int lastArc = nodeIn.numArcs - 1;

long lastArcStart = fstCompiler.bytes.getPosition();
int maxBytesPerArc = 0;
int maxBytesPerArcWithoutLabel = 0;
for (int arcIdx = 0; arcIdx < nodeIn.numArcs; arcIdx++) {
final FSTCompiler.Arc<T> arc = nodeIn.arcs[arcIdx];
final FSTCompiler.CompiledNode target = (FSTCompiler.CompiledNode) arc.target;
int flags = 0;
// System.out.println(" arc " + arcIdx + " label=" + arc.label + " -> target=" +
// target.node);

if (arcIdx == lastArc) {
flags += BIT_LAST_ARC;
}

if (fstCompiler.lastFrozenNode == target.node && !doFixedLengthArcs) {
// TODO: for better perf (but more RAM used) we
// could avoid this except when arc is "near" the
// last arc:
flags += BIT_TARGET_NEXT;
}

if (arc.isFinal) {
flags += BIT_FINAL_ARC;
if (arc.nextFinalOutput != NO_OUTPUT) {
flags += BIT_ARC_HAS_FINAL_OUTPUT;
}
} else {
assert arc.nextFinalOutput == NO_OUTPUT;
}

boolean targetHasArcs = target.node > 0;

if (!targetHasArcs) {
flags += BIT_STOP_NODE;
}

if (arc.output != NO_OUTPUT) {
flags += BIT_ARC_HAS_OUTPUT;
}

fstCompiler.bytes.writeByte((byte) flags);
long labelStart = fstCompiler.bytes.getPosition();
writeLabel(fstCompiler.bytes, arc.label);
int numLabelBytes = (int) (fstCompiler.bytes.getPosition() - labelStart);

// System.out.println(" write arc: label=" + (char) arc.label + " flags=" + flags + "
// target=" + target.node + " pos=" + bytes.getPosition() + " output=" +
// outputs.outputToString(arc.output));

if (arc.output != NO_OUTPUT) {
outputs.write(arc.output, fstCompiler.bytes);
// System.out.println(" write output");
}

if (arc.nextFinalOutput != NO_OUTPUT) {
// System.out.println(" write final output");
outputs.writeFinalOutput(arc.nextFinalOutput, fstCompiler.bytes);
}

if (targetHasArcs && (flags & BIT_TARGET_NEXT) == 0) {
assert target.node > 0;
// System.out.println(" write target");
fstCompiler.bytes.writeVLong(target.node);
}

// just write the arcs "like normal" on first pass, but record how many bytes each one took
// and max byte size:
if (doFixedLengthArcs) {
int numArcBytes = (int) (fstCompiler.bytes.getPosition() - lastArcStart);
fstCompiler.numBytesPerArc[arcIdx] = numArcBytes;
fstCompiler.numLabelBytesPerArc[arcIdx] = numLabelBytes;
lastArcStart = fstCompiler.bytes.getPosition();
maxBytesPerArc = Math.max(maxBytesPerArc, numArcBytes);
maxBytesPerArcWithoutLabel =
Math.max(maxBytesPerArcWithoutLabel, numArcBytes - numLabelBytes);
// System.out.println(" arcBytes=" + numArcBytes + " labelBytes=" + numLabelBytes);
}
}

// TODO: try to avoid wasteful cases: disable doFixedLengthArcs in that case
/*
*
* LUCENE-4682: what is a fair heuristic here?
* It could involve some of these:
* 1. how "busy" the node is: nodeIn.inputCount relative to frontier[0].inputCount?
* 2. how much binSearch saves over scan: nodeIn.numArcs
* 3. waste: numBytes vs numBytesExpanded
*
* the one below just looks at #3
if (doFixedLengthArcs) {
// rough heuristic: make this 1.25 "waste factor" a parameter to the phd ctor????
int numBytes = lastArcStart - startAddress;
int numBytesExpanded = maxBytesPerArc * nodeIn.numArcs;
if (numBytesExpanded > numBytes*1.25) {
doFixedLengthArcs = false;
}
}
*/

if (doFixedLengthArcs) {
assert maxBytesPerArc > 0;
// 2nd pass just "expands" all arcs to take up a fixed byte size

int labelRange = nodeIn.arcs[nodeIn.numArcs - 1].label - nodeIn.arcs[0].label + 1;
assert labelRange > 0;
if (shouldExpandNodeWithDirectAddressing(
fstCompiler, nodeIn, maxBytesPerArc, maxBytesPerArcWithoutLabel, labelRange)) {
writeNodeForDirectAddressing(
fstCompiler, nodeIn, startAddress, maxBytesPerArcWithoutLabel, labelRange);
fstCompiler.directAddressingNodeCount++;
} else {
writeNodeForBinarySearch(fstCompiler, nodeIn, startAddress, maxBytesPerArc);
fstCompiler.binarySearchNodeCount++;
}
}

final long thisNodeAddress = fstCompiler.bytes.getPosition() - 1;
fstCompiler.bytes.reverse(startAddress, thisNodeAddress);
fstCompiler.nodeCount++;
return thisNodeAddress;
}

Arc<T> 描述的是一个弧

//package org.apache.lucene.util.fst;
// org\apache\lucene\util\fst\FSTCompiler.java
/** Expert: holds a pending (seen but not yet serialized) arc. */
static class Arc<T> {
int label; // really an "unsigned" byte // 一个label
Node target; // 举例:a -> b 那么target 就是b
boolean isFinal;
T output;
T nextFinalOutput;
}

例子

cat的权重是5
dog的权重是7
dogs的权重是13

   String inputValues[] = {"cat", "dog", "dogs"};
long[] outputValues = {5, 7, 13};

下面是一个cat的例子 cat包含三个字符c,a ,t,分别代表三个ASCII码:

  • c:99
  • a:97
  • t:116

定义Arc: 一个弧包含三个内容: [a:label1]->[b:label2] 来描述一个弧:a指向b .其中a的值是label1, b的值是label2

下面是idea的截图, [2747:c] -> [2856:a] -> [2860:t]

fst linklist

下面是fst.bytes 的例子

fst bytes

最后是变成下列格式:

[0, 116, 15, 97, 6, 6, 115, 31, 103, 7, 111, 6, 7, 100, 22, 4, 5, 99, 16]

相关阅读

java 数组声明位置区别

· One min read

背景

  String inputValues[] = {"cat", "dog", "dogs"};

当我声明一个数组,方括号在右边的时候, idea会有个很小警告:

C-style array declaration of local variable 'inputValues' 

IDE劝我改成

  String[] inputValues = {"cat", "dog", "dogs"};

那么他们有什么区别呢?

我查了stackoverflow

这两个是等价的

jls是如何定义的?

相关引用

flink 编译

· 2 min read

背景

了解flink的使用

编译

下载代码:

git clone https://github.com/apache/flink.git
cd flink
## 编译
./mvnw -Drat.skip=true -DskipTests=true package

启动

## 切换目录
cd build-target
## 启动
./bin/start-cluster.sh
## 执行例子
./bin/flink run ./examples/batch/WordCount.jar


## 打开网站
http://127.0.0.1:8081/#/overview

输出

./bin/flink run ./examples/batch/WordCount.jar

创建demo

mvn archetype:generate \
-DarchetypeGroupId=org.apache.flink \
-DarchetypeArtifactId=flink-walkthrough-datastream-java \
-DarchetypeVersion=1.18.0 \
-DgroupId=frauddetection \
-DartifactId=frauddetection \
-Dversion=0.1 \
-Dpackage=spendreport \
-DinteractiveMode=false

  • 堆栈
filter:21, Example$1 (spendreport)
filter:18, Example$1 (spendreport)
processElement:38, StreamFilter (org.apache.flink.streaming.api.operators)
accept:-1, RecordProcessorUtils$$Lambda/0x00007f79f865e830 (org.apache.flink.streaming.runtime.io)
emitRecord:237, OneInputStreamTask$StreamTaskNetworkOutput (org.apache.flink.streaming.runtime.tasks)
processElement:146, AbstractStreamTaskNetworkInput (org.apache.flink.streaming.runtime.io)
emitNext:110, AbstractStreamTaskNetworkInput (org.apache.flink.streaming.runtime.io)
processInput:65, StreamOneInputProcessor (org.apache.flink.streaming.runtime.io)
processInput:562, StreamTask (org.apache.flink.streaming.runtime.tasks)
runDefaultAction:-1, StreamTask$$Lambda/0x00007f79f863ba10 (org.apache.flink.streaming.runtime.tasks)
runMailboxLoop:231, MailboxProcessor (org.apache.flink.streaming.runtime.tasks.mailbox)
runMailboxLoop:858, StreamTask (org.apache.flink.streaming.runtime.tasks)
invoke:807, StreamTask (org.apache.flink.streaming.runtime.tasks)
run:-1, Task$$Lambda/0x00007f79f8695ff8 (org.apache.flink.runtime.taskmanager)
runWithSystemExitMonitoring:953, Task (org.apache.flink.runtime.taskmanager)
restoreAndInvoke:932, Task (org.apache.flink.runtime.taskmanager)
doRun:746, Task (org.apache.flink.runtime.taskmanager)
run:562, Task (org.apache.flink.runtime.taskmanager)
runWith:1596, Thread (java.lang)
run:1583, Thread (java.lang)

相关阅读

insert ignore 死锁

· 3 min read

背景

insert ignore sql死锁了

排查

日志

排查了死锁日志: 我们定位到是有个表,简单来说,是有两个字段:

  • id : 主键
  • email_address: 唯一索引

一共有两个线程在写入,每次的操作就是批量用insert ignore写入,初步看都是很简单的sql, 后面google一下发现是和间隙锁有关: 相关blog

2023-05-03 05:06:45 0x4002719ffef0
*** (1) TRANSACTION:
TRANSACTION 218982374, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 7 lock struct(s), heap size 1136, 4 row lock(s)
MySQL thread id 481964, OS thread handle 70370189385456, query id 1320790141 10.4.3.228 app_cdp_0 update
insert ignore into cdp_user_email
(email_address,
clean_tag,
is_upload_emarsys,
created_at,
upload_at
)
value


*** (1) HOLDS THE LOCK(S):
RECORD LOCKS space id 189 page no 328322 n bits 272 index PRIMARY of table `customer_data_platform`.`cdp_user_email` trx id 218982374 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;


*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 189 page no 328322 n bits 272 index PRIMARY of table `customer_data_platform`.`cdp_user_email` trx id 218982374 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;


*** (2) TRANSACTION:
TRANSACTION 218982373, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 11 lock struct(s), heap size 1136, 6 row lock(s)
MySQL thread id 481963, OS thread handle 70370189655792, query id 1320790139 10.4.3.228 app_cdp_0 update
insert ignore into cdp_user_email
(email_address,
clean_tag,
is_upload_emarsys,
created_at,
upload_at
)
value


*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 189 page no 328322 n bits 272 index PRIMARY of table `customer_data_platform`.`cdp_user_email` trx id 218982373 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;


*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 189 page no 328322 n bits 272 index PRIMARY of table `customer_data_platform`.`cdp_user_email` trx id 218982373 lock_mode X insert intention waiting
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
0: len 8; hex 73757072656d756d; asc supremum;;

*** WE ROLL BACK TRANSACTION (1)

修改方式

将批量insert 改成每次只写入一条

volatile java 实现

· 17 min read

背景

了解java volatile实现

实现

核心在这里: is_volatile_shift

void TemplateTable::putfield_or_static(int byte_no, bool is_static, RewriteControl rc) {
transition(vtos, vtos);

const Register cache = rcx;
const Register index = rdx;
const Register obj = rcx;
const Register off = rbx;
const Register flags = rax;

resolve_cache_and_index(byte_no, cache, index, sizeof(u2));
jvmti_post_field_mod(cache, index, is_static);
load_field_cp_cache_entry(obj, cache, index, off, flags, is_static);

Label notVolatile, Done;
__ movl(rdx, flags);
__ shrl(rdx, ConstantPoolCacheEntry::is_volatile_shift); // 右移volatile 标志位 , 然后如果是volatile ,则走到不太分支
__ andl(rdx, 0x1);

// Check for volatile store
__ testl(rdx, rdx);
__ jcc(Assembler::zero, notVolatile);

putfield_or_static_helper(byte_no, is_static, rc, obj, off, flags);
volatile_barrier(Assembler::Membar_mask_bits(Assembler::StoreLoad |
Assembler::StoreStore));
__ jmp(Done);
__ bind(notVolatile);

putfield_or_static_helper(byte_no, is_static, rc, obj, off, flags);

__ bind(Done);
}

这是具体的代码:

// ----------------------------------------------------------------------------
// Volatile variables demand their effects be made known to all CPU's
// in order. Store buffers on most chips allow reads & writes to
// reorder; the JMM's ReadAfterWrite.java test fails in -Xint mode
// without some kind of memory barrier (i.e., it's not sufficient that
// the interpreter does not reorder volatile references, the hardware
// also must not reorder them).
//
// According to the new Java Memory Model (JMM):
// (1) All volatiles are serialized wrt to each other. ALSO reads &
// writes act as aquire & release, so:
// (2) A read cannot let unrelated NON-volatile memory refs that
// happen after the read float up to before the read. It's OK for
// non-volatile memory refs that happen before the volatile read to
// float down below it.
// (3) Similar a volatile write cannot let unrelated NON-volatile
// memory refs that happen BEFORE the write float down to after the
// write. It's OK for non-volatile memory refs that happen after the
// volatile write to float up before it.
//
// We only put in barriers around volatile refs (they are expensive),
// not _between_ memory refs (that would require us to track the
// flavor of the previous memory refs). Requirements (2) and (3)
// require some barriers before volatile stores and after volatile
// loads. These nearly cover requirement (1) but miss the
// volatile-store-volatile-load case. This final case is placed after
// volatile-stores although it could just as well go before
// volatile-loads.

void TemplateTable::volatile_barrier(Assembler::Membar_mask_bits order_constraint ) {
// Helper function to insert a is-volatile test and memory barrier
__ membar(order_constraint);
}

还有这段:

ConstantPoolCache 看上去是运行时从.class文件读取的内容, 这里就有volatile的标志位,我们看看是在哪里设置这个值ConstantPoolCacheEntry::is_volatile_shift

// The ConstantPoolCache is not a cache! It is the resolution table that the
// interpreter uses to avoid going into the runtime and a way to access resolved
// values.

// A ConstantPoolCacheEntry describes an individual entry of the constant
// pool cache. There's 2 principal kinds of entries: field entries for in-
// stance & static field access, and method entries for invokes. Some of
// the entry layout is shared and looks as follows:
//
// bit number |31 0|
// bit length |-8--|-8--|---16----|
// --------------------------------
// _indices [ b2 | b1 | index ] index = constant_pool_index
// _f1 [ entry specific ] metadata ptr (method or klass)
// _f2 [ entry specific ] vtable or res_ref index, or vfinal method ptr
// _flags [tos|0|F=1|0|0|0|f|v|0 |0000|field_index] (for field entries)
// bit length [ 4 |1| 1 |1|1|1|1|1|1 |1 |-3-|----16-----]
// _flags [tos|0|F=0|S|A|I|f|0|vf|indy_rf|000|00000|psize] (for method entries)
// bit length [ 4 |1| 1 |1|1|1|1|1|1 |-4--|--8--|--8--]

// --------------------------------
//
// with:
// index = original constant pool index
// b1 = bytecode 1
// b2 = bytecode 2
// psize = parameters size (method entries only)
// field_index = index into field information in holder InstanceKlass
// The index max is 0xffff (max number of fields in constant pool)
// and is multiplied by (InstanceKlass::next_offset) when accessing.
// tos = TosState
// F = the entry is for a field (or F=0 for a method)
// A = call site has an appendix argument (loaded from resolved references)
// I = interface call is forced virtual (must use a vtable index or vfinal)
// f = field or method is final
// v = field is volatile
// vf = virtual but final (method entries only: is_vfinal())
// indy_rf = call site specifier method resolution failed
//
// The flags after TosState have the following interpretation:
// bit 27: 0 for fields, 1 for methods
// f flag true if field is marked final
// v flag true if field is volatile (only for fields)
// f2 flag true if f2 contains an oop (e.g., virtual final method)
// fv flag true if invokeinterface used for method in class Object
//
// The flags 31, 30, 29, 28 together build a 4 bit number 0 to 16 with the
// following mapping to the TosState states:
//
// btos: 0
// ztos: 1
// ctos: 2
// stos: 3
// itos: 4
// ltos: 5
// ftos: 6
// dtos: 7
// atos: 8
// vtos: 9
//
// Entry specific: field entries:
// _indices = get (b1 section) and put (b2 section) bytecodes, original constant pool index
// _f1 = field holder (as a java.lang.Class, not a Klass*)
// _f2 = field offset in bytes
// _flags = field type information, original FieldInfo index in field holder
// (field_index section)
//
// Entry specific: method entries:
// _indices = invoke code for f1 (b1 section), invoke code for f2 (b2 section),
// original constant pool index
// _f1 = Method* for non-virtual calls, unused by virtual calls.
// for interface calls, which are essentially virtual but need a klass,
// contains Klass* for the corresponding interface.
// for invokedynamic and invokehandle, f1 contains the adapter method which
// manages the actual call. The appendix is stored in the ConstantPool
// resolved_references array.
// (upcoming metadata changes will move the appendix to a separate array)
// _f2 = vtable/itable index (or final Method*) for virtual calls only,
// unused by non-virtual. The is_vfinal flag indicates this is a
// method pointer for a final method, not an index.
// _flags = has local signature (MHs and indy),
// virtual final bit (vfinal),
// parameter size (psize section)
//
// Note: invokevirtual & invokespecial bytecodes can share the same constant
// pool entry and thus the same constant pool cache entry. All invoke
// bytecodes but invokevirtual use only _f1 and the corresponding b1
// bytecode, while invokevirtual uses only _f2 and the corresponding
// b2 bytecode. The value of _flags is shared for both types of entries.
//
// The fields are volatile so that they are stored in the order written in the
// source code. The _indices field with the bytecode must be written last.

class CallInfo;

class ConstantPoolCacheEntry {

然后我们看看设置值的地方在这里

 //源码地址   src/hotspot/share/oops/cpCache.cpp

// Note that concurrent update of both bytecodes can leave one of them
// reset to zero. This is harmless; the interpreter will simply re-resolve
// the damaged entry. More seriously, the memory synchronization is needed
// to flush other fields (f1, f2) completely to memory before the bytecodes
// are updated, lest other processors see a non-zero bytecode but zero f1/f2.
void ConstantPoolCacheEntry::set_field(Bytecodes::Code get_code,
Bytecodes::Code put_code,
Klass* field_holder,
int field_index,
int field_offset,
TosState field_type,
bool is_final,
bool is_volatile) {
set_f1(field_holder);
set_f2(field_offset);
assert((field_index & field_index_mask) == field_index,
"field index does not fit in low flag bits");
set_field_flags(field_type,
((is_volatile ? 1 : 0) << is_volatile_shift) | <--- 在这里会设置volatile 的值
((is_final ? 1 : 0) << is_final_shift),
field_index);
set_bytecode_1(get_code);
set_bytecode_2(put_code);
NOT_PRODUCT(verify(tty));
}

然后用gdb调试一下:

(gdb) bt
#0 b ::set_field (this=0x7fffb4151b50, get_code=Bytecodes::_nop, put_code=Bytecodes::_nop, field_holder=0x800042fa0, field_index=2, field_offset=120, field_type=atos,
is_final=true, is_volatile=false) at /home/dai/jdk/src/hotspot/share/oops/cpCache.cpp:139
#1 0x00007ffff65d2561 in InterpreterRuntime::resolve_get_put (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:708
#2 0x00007ffff65d3e14 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:959
#3 0x00007fffe10203e3 in ?? ()
#4 0x00007ffff7bcc0a0 in TemplateInterpreter::_active_table () from /home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#5 0x00007fffe1020362 in ?? ()
#6 0x0000000000000000 in ?? ()

我们看看ConstantPoolCacheEntry是从哪里取的

// Helper class to access current interpreter state
class LastFrameAccessor : public StackObj {
frame _last_frame;
public:
...
ConstantPoolCacheEntry* cache_entry_at(int i) const
{ return method()->constants()->cache()->entry_at(i); }
ConstantPoolCacheEntry* cache_entry() const { return cache_entry_at(Bytes::get_native_u2(bcp() + 1)); }
}
(gdb) p m->print()
{method}
- this oop: 0x00007fffd14108e8
- method holder: 'java/lang/String'
- constants: 0x00007fffd14013b8 constant pool [1396]/operands[28] {0x00007fffd14013b8} for 'java/lang/String' cache=0x00007fffd1544158
- access: 0x8 static
- name: '<clinit>'
- signature: '()V'
- max stack: 3
- max locals: 0
- size of params: 0
- method size: 13
- vtable index: -2
- i2i entry: 0x00007fffe100dbe0
- adapters: AHE@0x00007ffff009b550: 0x i2c: 0x00007fffe1115060 c2i: 0x00007fffe111510d c2iUV: 0x00007fffe11150e0 c2iNCI: 0x00007fffe111514a
- compiled entry 0x00007fffe111510d
- code size: 22
- code start: 0x00007fffd14108c0
- code end (excl): 0x00007fffd14108d6
- checked ex length: 0
- linenumber start: 0x00007fffd14108d6
- localvar length: 0
$7 = void
(gdb) bt
#0 frame::interpreter_frame_method (this=0x7ffff5aa43f8) at /home/ubuntu/daixiao/jdk/src/hotspot/share/runtime/frame.cpp:332
#1 0x00007ffff672253c in LastFrameAccessor::method (this=0x7ffff5aa43f0) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:90
#2 0x00007ffff671d2fb in InterpreterRuntime::resolve_get_put (current=0x7ffff0028940, bytecode=Bytecodes::_putstatic) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:650
#3 0x00007ffff671ec81 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028940, bytecode=Bytecodes::_putstatic) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:948
#4 0x00007fffe10203a3 in ?? ()
#5 0x00007ffff7d4b280 in TemplateInterpreter::_active_table () from /home/ubuntu/daixiao/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#6 0x00007fffe1020322 in ?? ()
#7 0x0000000000000001 in ?? ()
#8 0x00007ffff5aa44d8 in ?? ()
#9 0x00007fffd14108c1 in ?? ()
#10 0x00007ffff5aa4528 in ?? ()
#11 0x00007fffd1544158 in ?? ()
#12 0x0000000000000000 in ?? ()
(gdb)

解析cache

InterpreterRuntime::resolve_from_cache

LinkResolver::resolve_field_access

相关类在

//   src/hotspot/share/oops/cpCache.cpp
// 头文件 在 src/hotspot/share/oops/cpCache.hpp
ConstantPoolCacheEntry

问了一下chatgpt

在HotSpot JVM中,ConstantPoolCacheEntry对象是在解析类文件时动态创建的。具体来说,在解析类文件时,JVM会为类文件中的每个常量创建一个对应的ConstantPoolCacheEntry对象,并将其添加到类的常量池缓存中。

ConstantPoolCacheEntry对象的实例化主要是在ClassFileParser类的parse_constant_pool_entry()函数中完成的。在解析常量池时,parse_constant_pool_entry()函数会根据常量的类型和值动态创建一个对应的ConstantPoolCacheEntry对象,并将其添加到类的常量池缓存中。

需要注意的是,在解析过程中,JVM可能会使用占位符对象来表示无效的或未知的常量,而不是创建ConstantPoolCacheEntry对象。在这种情况下,JVM会将占位符对象添加到常量池缓存中,以便在解析和执行时可以快速跳过无效的或未知的常量。

总之,在HotSpot JVM中,ConstantPoolCacheEntry对象的实例化是在解析类文件时完成的,而具体实现是在ClassFileParser类的parse_constant_pool_entry()函数中完成的。

查看堆栈

(gdb) p stream->_source
$4 = 0x7ffff00bdd40 "/home/ubuntu/daixiao/jdk/build/linux-x86_64-server-slowdebug/jdk/modules/java.base"
(gdb) bt
#0 ClassFileParser::parse_constant_pool_entries (this=0x7ffff0028940, stream=0x0, cp=0x7ffff5fadff1 <Thread::as_Java_thread()+39>, length=32767, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:150
#1 0x00007ffff6378b5e in ClassFileParser::parse_constant_pool (this=0x7ffff5aa27e0, stream=0x7ffff00298e0, cp=0x7fffd169b1c8, length=31, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:424
#2 0x00007ffff638aacc in ClassFileParser::parse_stream (this=0x7ffff5aa27e0, stream=0x7ffff00298e0, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:5720
#3 0x00007ffff638a2b1 in ClassFileParser::ClassFileParser (this=0x7ffff5aa27e0, stream=0x7ffff00298e0, name=0x7fffb005b0a0, loader_data=0x7ffff0091890, cl_info=0x7ffff5aa2a10, pub_level=ClassFileParser::BROADCAST, __the_thread__=0x7ffff0028940)
at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classFileParser.cpp:5590
#4 0x00007ffff69d5f1f in KlassFactory::create_from_stream (stream=0x7ffff00298e0, name=0x7fffb005b0a0, loader_data=0x7ffff0091890, cl_info=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/klassFactory.cpp:199
#5 0x00007ffff639a9e5 in ClassLoader::load_class (name=0x7fffb005b0a0, search_append_only=false, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/classLoader.cpp:1222
#6 0x00007ffff6e97300 in SystemDictionary::load_instance_class_impl (class_name=0x7fffb005b0a0, class_loader=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:1290
#7 0x00007ffff6e976d1 in SystemDictionary::load_instance_class (name_hash=1923324215, name=0x7fffb005b0a0, class_loader=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:1356
#8 0x00007ffff6e95874 in SystemDictionary::resolve_instance_class_or_null (name=0x7fffb005b0a0, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:724
#9 0x00007ffff6e94481 in SystemDictionary::resolve_instance_class_or_null_helper (class_name=0x7fffb005b0a0, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:295
#10 0x00007ffff6e94330 in SystemDictionary::resolve_or_null (class_name=0x7fffb005b0a0, class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:278
#11 0x00007ffff6e94273 in SystemDictionary::resolve_or_fail (class_name=0x7fffb005b0a0, class_loader=..., protection_domain=..., throw_error=true, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/systemDictionary.cpp:264
#12 0x00007ffff64314d1 in ConstantPool::klass_at_impl (this_cp=..., which=506, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/oops/constantPool.cpp:512
#13 0x00007ffff62aca88 in ConstantPool::klass_at (this=0x7fffd1692c38, which=506, __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/oops/constantPool.hpp:420
#14 0x00007ffff671af3a in InterpreterRuntime::_new (current=0x7ffff0028940, pool=0x7fffd1692c38, index=506) at /home/ubuntu/daixiao/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:219
#15 0x00007fffe1023b92 in ?? ()
#16 0x00007fffe1023b06 in ?? ()
#17 0x00007ffff5aa3368 in ?? ()
#18 0x00007fffd1699fe1 in ?? ()
#19 0x00007ffff5aa33b8 in ?? ()
#20 0x00007fffd169a0a0 in ?? ()
#21 0x0000000000000000 in ?? ()

设置断点

(gdb) commands
Type commands for breakpoint(s) 5, one per line.
End with a line saying just "end".
>p name->print()
>c
>end

打印

Thread 2 "java" hit Breakpoint 5, KlassFactory::create_from_stream (stream=0x7ffff5aa2f20, name=0x7ffff044c4a0, loader_data=0x7ffff03ddf90, cl_info=..., __the_thread__=0x7ffff0028940) at /home/ubuntu/daixiao/jdk/src/hotspot/share/classfile/klassFactory.cpp:172
172 assert(loader_data != NULL, "invariant");
Symbol: 'com/HelloWorld' count 2$1035 = void

类加载

$801 = 0x7ffff59fd0b0 "file:/home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/bin/"
(gdb) bt
#0 KlassFactory::create_from_stream (stream=0x7ffff59fce00, name=0x7ffff057a9d0, loader_data=0x7ffff05185c0, cl_info=..., __the_thread__=0x7ffff0028f50)
at /home/dai/jdk/src/hotspot/share/classfile/klassFactory.cpp:179
#1 0x00007ffff6d4248e in SystemDictionary::resolve_class_from_stream (st=0x7ffff59fce00, class_name=0x7ffff057a9d0, class_loader=..., cl_info=..., __the_thread__=0x7ffff0028f50)
at /home/dai/jdk/src/hotspot/share/classfile/systemDictionary.cpp:914
#2 0x00007ffff6d42708 in SystemDictionary::resolve_from_stream (st=0x7ffff59fce00, class_name=0x7ffff057a9d0, class_loader=..., cl_info=..., __the_thread__=0x7ffff0028f50)
at /home/dai/jdk/src/hotspot/share/classfile/systemDictionary.cpp:952
#3 0x00007ffff66e1b51 in jvm_define_class_common (name=0x7ffff59fd030 "Hello", loader=0x7ffff59fd5a0, buf=0x7ffff04a4b90 "\312\376\272\276", len=409, pd=0x7ffff59fd578,
source=0x7ffff59fd0b0 "file:/home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/bin/", __the_thread__=0x7ffff0028f50) at /home/dai/jdk/src/hotspot/share/prims/jvm.cpp:883
#4 0x00007ffff66e2832 in JVM_DefineClassWithSource (env=0x7ffff0029230, name=0x7ffff59fd030 "Hello", loader=0x7ffff59fd5a0, buf=0x7ffff04a4b90 "\312\376\272\276", len=409, pd=0x7ffff59fd578,
source=0x7ffff59fd0b0 "file:/home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/bin/") at /home/dai/jdk/src/hotspot/share/prims/jvm.cpp:1047
#5 0x00007ffff58db1f5 in Java_java_lang_ClassLoader_defineClass1 (env=0x7ffff0029230, cls=0x7ffff59fd560, loader=0x7ffff59fd5a0, name=0x7ffff59fd598, data=0x7ffff59fd590, offset=0, length=409,
pd=0x7ffff59fd578, source=0x7ffff59fd570) at /home/dai/jdk/src/java.base/share/native/libjava/ClassLoader.c:132
#6 0x00007fffe100f6cb in ?? ()
#7 0x0000000000000199 in ?? ()
#8 0x00007ffff59fd578 in ?? ()
#9 0x00007ffff59fd570 in ?? ()
#10 0x0000555555581230 in ?? ()
#11 0x00007ffff0028f50 in ?? ()
#12 0x00007fffb445ba08 in ?? ()
#13 0x00007fffe100f199 in ?? ()
#14 0x00007ffff59fd508 in ?? ()
#15 0x00007fffb4025170 in ?? ()
#16 0x00007ffff59fd5a0 in ?? ()
#17 0x00007fffb4147588 in ?? ()
#18 0x0000000000000000 in ?? ()

fileinfo 如何初始化

ConstantPoolCacheEntry 是由fieldDescriptor 的字段传进去的 , 那么我们看看fieldDescriptor是怎么初始化的

  fieldDescriptor info;
...
void InterpreterRuntime::resolve_get_put(JavaThread* current, Bytecodes::Code bytecode) {
...

LinkResolver::resolve_field_access(info, pool, last_frame.get_index_u2_cpcache(bytecode),
m, bytecode, CHECK);
...


}

我们最后找到初始化的地方

bool InstanceKlass::find_local_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
Symbol* f_name = fs.name();
Symbol* f_sig = fs.signature();
if (f_name == name && f_sig == sig) {
fd->reinitialize(const_cast<InstanceKlass*>(this), fs.index()); // 初始化的地方
return true;
}
}
return false;
}

初始化就在这里: fieldDescriptor::reinitialize

void fieldDescriptor::reinitialize(InstanceKlass* ik, int index) {
...
FieldInfo* f = ik->field(index);
_access_flags = accessFlags_from(f->access_flags());
guarantee(f->name_index() != 0 && f->signature_index() != 0, "bad constant pool index for fieldDescriptor");
_index = index;
...
}

jvm 堆栈结构

inline frame ContinuationEntry::to_frame() const {
static CodeBlob* cb = CodeCache::find_blob_fast(entry_pc());
assert(cb != nullptr, "");
assert(cb->as_compiled_method()->method()->is_continuation_enter_intrinsic(), "");
return frame(entry_sp(), entry_sp(), entry_fp(), entry_pc(), cb);
}
(gdb) bt
#0 frame::frame (this=0x7ffff59fe348) at /home/dai/jdk/src/hotspot/cpu/x86/frame_x86.inline.hpp:37
#1 0x00007ffff65d76f1 in LastFrameAccessor::LastFrameAccessor (this=0x7ffff59fe340, current=0x7ffff0028f70)
at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:84
#2 0x00007ffff65d212e in InterpreterRuntime::resolve_get_put (current=0x7ffff0028f70,
bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:652
#3 0x00007ffff65d3e14 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028f70,
bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:959
#4 0x00007fffe10203e3 in ?? ()
#5 0x00007ffff7bca0a0 in TemplateInterpreter::_active_table ()
from /home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#6 0x00007fffe1020362 in ?? ()
#7 0x0000000000000001 in ?? ()
#8 0x00007ffff59fe438 in ?? ()
#9 0x00007fffb4010821 in ?? ()
#10 0x00007ffff59fe488 in ?? ()
#11 0x00007fffb4149b38 in ?? ()
#12 0x0000000000000000 in ?? ()

后面我们能看到初始化是在这里:

frame JavaThread::pd_last_frame() {
assert(has_last_Java_frame(), "must have last_Java_sp() when suspended");
vmassert(_anchor.last_Java_pc() != NULL, "not walkable");
return frame(_anchor.last_Java_sp(), _anchor.last_Java_fp(), _anchor.last_Java_pc());
}

frame 的初始化

(gdb) bt
#0 JavaThread::pd_last_frame (this=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/os_cpu/linux_x86/thread_linux_x86.cpp:30
#1 0x00007ffff612888b in JavaThread::last_frame (this=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/share/runtime/thread.hpp:1407
#2 0x00007ffff65d7757 in LastFrameAccessor::LastFrameAccessor (this=0x7ffff59fe340, current=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:86
#3 0x00007ffff65d212e in InterpreterRuntime::resolve_get_put (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:652
#4 0x00007ffff65d3e14 in InterpreterRuntime::resolve_from_cache (current=0x7ffff0028f70, bytecode=Bytecodes::_putstatic) at /home/dai/jdk/src/hotspot/share/interpreter/interpreterRuntime.cpp:959
#5 0x00007fffe10203e3 in ?? ()
#6 0x00007ffff7bca0a0 in TemplateInterpreter::_active_table () from /home/dai/jdk/build/linux-x86_64-server-slowdebug/jdk/lib/server/libjvm.so
#7 0x00007fffe1020362 in ?? ()
#8 0x0000000000000001 in ?? ()
#9 0x00007ffff59fe438 in ?? ()
#10 0x00007fffb4010821 in ?? ()
#11 0x00007ffff59fe488 in ?? ()
#12 0x00007fffb4149b38 in ?? ()
#13 0x0000000000000000 in ?? ()
(gdb) list
35 // For Forte Analyzer AsyncGetCallTrace profiling support - thread is
36 // currently interrupted by SIGPROF
37 bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr,
38 void* ucontext, bool isInJava) {
39
40 assert(Thread::current() == this, "caller must be current thread");
41 return pd_get_top_frame(fr_addr, ucontext, isInJava);
42 }
43
44 bool JavaThread::pd_get_top_frame_for_profiling(frame* fr_addr, void* ucontext, bool isInJava) {
(gdb) info registers
rax 0x7ffff59fe220 140737314284064
rbx 0x7ffff59fe340 140737314284352
rcx 0x7ffff00153f0 140737220006896
rdx 0x7ffff0028f70 140737220087664
rsi 0x7ffff0028f70 140737220087664
rdi 0x7ffff59fe220 140737314284064
rbp 0x7ffff59fe1f0 0x7ffff59fe1f0
rsp 0x7ffff59fe1d0 0x7ffff59fe1d0
r8 0x8 8
r9 0x0 0
r10 0x7ffff7bca0a0 140737349722272
r11 0x7ffff0000090 140737219920016
r12 0x0 0
r13 0x7fffb4010821 140736213354529
r14 0x7ffff59fe488 140737314284680
r15 0x7ffff0028f70 140737220087664
rip 0x7ffff6da2ea3 0x7ffff6da2ea3 <JavaThread::pd_last_frame()+23>
eflags 0x202 [ IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0

然后我们看到在

  JavaFrameAnchor _anchor;    

class JavaFrameAnchor {
...

private:
...
intptr_t* volatile _last_Java_sp; //stack_pointer
...
}

JavaFrameAnchor 初始化

JavaFrameAnchor::clear (this=0x7ffff0029230) at /home/dai/jdk/src/hotspot/cpu/x86/javaFrameAnchor_x86.hpp:44
44 _last_Java_fp = NULL;
(gdb) bt
#0 JavaFrameAnchor::clear (this=0x7ffff0029230) at /home/dai/jdk/src/hotspot/cpu/x86/javaFrameAnchor_x86.hpp:44
#1 0x00007ffff65df910 in JavaFrameAnchor::JavaFrameAnchor (this=0x7ffff0029230) at /home/dai/jdk/src/hotspot/share/runtime/javaFrameAnchor.hpp:88
#2 0x00007ffff6d8730c in JavaThread::JavaThread (this=0x7ffff0028f70) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:1076
#3 0x00007ffff6d8d0ee in Threads::create_vm (args=0x7ffff59fed50, canTryAgain=0x7ffff59fec5b) at /home/dai/jdk/src/hotspot/share/runtime/thread.cpp:2817
#4 0x00007ffff66b243b in JNI_CreateJavaVM_inner (vm=0x7ffff59feda8, penv=0x7ffff59fedb0, args=0x7ffff59fed50) at /home/dai/jdk/src/hotspot/share/prims/jni.cpp:3613
#5 0x00007ffff66b2787 in JNI_CreateJavaVM (vm=0x7ffff59feda8, penv=0x7ffff59fedb0, args=0x7ffff59fed50) at /home/dai/jdk/src/hotspot/share/prims/jni.cpp:3701
#6 0x00007ffff7faca6a in InitializeJVM (pvm=0x7ffff59feda8, penv=0x7ffff59fedb0, ifn=0x7ffff59fee00) at /home/dai/jdk/src/java.base/share/native/libjli/java.c:1459
#7 0x00007ffff7fa95ec in JavaMain (_args=0x7fffffffa9a0) at /home/dai/jdk/src/java.base/share/native/libjli/java.c:411
#8 0x00007ffff7fb05ec in ThreadJavaMain (args=0x7fffffffa9a0) at /home/dai/jdk/src/java.base/unix/native/libjli/java_md.c:651
#9 0x00007ffff7c94b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#10 0x00007ffff7d26a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

在这里会设置last_java_sp

void MacroAssembler::call_VM_base(Register oop_result,
Register java_thread,
Register last_java_sp,
address entry_point,
int number_of_arguments,
bool check_exceptions) {
// determine java_thread register
if (!java_thread->is_valid()) {
#ifdef _LP64
java_thread = r15_thread;
#else
java_thread = rdi;
get_thread(java_thread);
#endif // LP64
}
// determine last_java_sp register
if (!last_java_sp->is_valid()) {
last_java_sp = rsp;
}
// debugging support
assert(number_of_arguments >= 0 , "cannot have negative number of arguments");
LP64_ONLY(assert(java_thread == r15_thread, "unexpected register"));
#ifdef ASSERT
// TraceBytecodes does not use r12 but saves it over the call, so don't verify
// r12 is the heapbase.
LP64_ONLY(if (UseCompressedOops && !TraceBytecodes) verify_heapbase("call_VM_base: heap base corrupted?");)
#endif // ASSERT

assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result");
assert(java_thread != last_java_sp, "cannot use the same register for java_thread & last_java_sp");

// push java thread (becomes first argument of C function)

NOT_LP64(push(java_thread); number_of_arguments++);
LP64_ONLY(mov(c_rarg0, r15_thread));

// set last Java frame before call
assert(last_java_sp != rbp, "can't use ebp/rbp");

// Only interpreter should have to set fp
set_last_Java_frame(java_thread, last_java_sp, rbp, NULL);

// do the call, remove parameters
MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments);

// restore the thread (cannot use the pushed argument since arguments
// may be overwritten by C code generated by an optimizing compiler);
// however can use the register value directly if it is callee saved.
if (LP64_ONLY(true ||) java_thread == rdi || java_thread == rsi) {
// rdi & rsi (also r15) are callee saved -> nothing to do
#ifdef ASSERT
guarantee(java_thread != rax, "change this code");
push(rax);
{ Label L;
get_thread(rax);
cmpptr(java_thread, rax);
jcc(Assembler::equal, L);
STOP("MacroAssembler::call_VM_base: rdi not callee saved?");
bind(L);
}
pop(rax);
#endif
} else {
get_thread(java_thread);
}
// reset last Java frame
// Only interpreter should have to clear fp
reset_last_Java_frame(java_thread, true);

// C++ interp handles this in the interpreter
check_and_handle_popframe(java_thread);
check_and_handle_earlyret(java_thread);

if (check_exceptions) {
// check for pending exceptions (java_thread is set upon return)
cmpptr(Address(java_thread, Thread::pending_exception_offset()), (int32_t) NULL_WORD);
#ifndef _LP64
jump_cc(Assembler::notEqual,
RuntimeAddress(StubRoutines::forward_exception_entry()));
#else
// This used to conditionally jump to forward_exception however it is
// possible if we relocate that the branch will not reach. So we must jump
// around so we can always reach

Label ok;
jcc(Assembler::equal, ok);
jump(RuntimeAddress(StubRoutines::forward_exception_entry()));
bind(ok);
#endif // LP64
}

// get oop result if there is one and reset the value in the thread
if (oop_result->is_valid()) {
get_vm_result(oop_result, java_thread);
}
}

volatile 标志位

///home/dai/jdk/src/hotspot/share/utilities/accessFlags.hpp
bool is_volatile () const { return (_flags & JVM_ACC_VOLATILE ) != 0; }

相关阅读

java integer divison

· 4 min read

背景

了解java整除除法的规则

jls 描述

Integer division rounds toward 0. That is, the quotient produced for operands n
and d that are integers after binary numeric promotion (§5.6) is an integer value q
whose magnitude is as large as possible while satisfying |d ⋅ q| ≤ |n|. Moreover, q
is positive when |n| ≥ |d| and n and d have the same sign, but q is negative when
|n| ≥ |d| and n and d have opposite signs.

也就是

int res = 3 / 5 = 0 ;

最后使用的bytecode 是idiv

我们看看idiv 这个bytecode 是怎么实现的吧:

jvm 实现

void Assembler::idivl(Register src) {
int encode = prefix_and_encode(src->encoding());
emit_int16((unsigned char)0xF7, (0xF8 | encode));
}

void Assembler::divl(Register src) { // Unsigned
int encode = prefix_and_encode(src->encoding());
emit_int16((unsigned char)0xF7, (0xF0 | encode));
}

查看intel的文档: intel

2.1.5 Addressing-Mode Encoding of ModR/M and SIB Bytes
The values and corresponding addressing forms of the ModR/M and SIB bytes are shown in Table 2-1 through Table
2-3: 16-bit addressing forms specified by the ModR/M byte are in Table 2-1 and 32-bit addressing forms are in
Table 2-2. Table 2-3 shows 32-bit addressing forms specified by the SIB byte. In cases where the reg/opcode field
in the ModR/M byte represents an extended opcode, valid encodings are shown in Appendix B.
In Table 2-1 and Table 2-2, the Effective Address column lists 32 effective addresses that can be assigned to the
first operand of an instruction by using the Mod and R/M fields of the ModR/M byte. The first 24 options provide
ways of specifying a memory location; the last eight (Mod = 11B) provide ways of specifying general-purpose, MMX
technology and XMM registers.
The Mod and R/M columns in Table 2-1 and Table 2-2 give the binary encodings of the Mod and R/M fields required
to obtain the effective address listed in the first column. For example: see the row indicated by Mod = 11B, R/M =
000B. The row identifies the general-purpose registers EAX, AX or AL; MMX technology register MM0; or XMM
register XMM0. The register used is determined by the opcode byte and the operand-size attribute.
Now look at the seventh row in either table (labeled “REG =”). This row specifies the use of the 3-bit Reg/Opcode
field when the field is used to give the location of a second operand. The second operand must be a generalpurpose, MMX technology, or XMM register. Rows one through five list the registers that may correspond to the
value in the table. Again, the register used is determined by the opcode byte along with the operand-size attribute.
If the instruction does not require a second operand, then the Reg/Opcode field may be used as an opcode extension. This use is represented by the sixth row in the tables (labeled “/digit (Opcode)”). Note that values in row six
are represented in decimal form.
The body of Table 2-1 and Table 2-2 (under the label “Value of ModR/M Byte (in Hexadecimal)”) contains a 32 by
8 array that presents all of 256 values of the ModR/M byte (in hexadecimal). Bits 3, 4 and 5 are specified by the
column of the table in which a byte resides. The row specifies bits 0, 1 and 2; and bits 6 and 7. The figure below
demonstrates interpretation of one table value.

register 寄存器会从0编码到16,编码顺序如上述所示

例子

int main(void){                        
int num0 = 5;
int num1 = 2;
int num = num0 / num1 ;
return num;
}
## 编译
gcc -O0 test.c -o test

## 用objdump 列出汇编内容
objdump -d test | grep -A20 '<main>:'

结果为

0000000000001129 <main>:
1129: f3 0f 1e fa endbr64
112d: 55 push %rbp
112e: 48 89 e5 mov %rsp,%rbp
1131: c7 45 f4 05 00 00 00 movl $0x5,-0xc(%rbp)
1138: c7 45 f8 02 00 00 00 movl $0x2,-0x8(%rbp)
113f: 8b 45 f4 mov -0xc(%rbp),%eax
1142: 99 cltd
1143: f7 7d f8 idivl -0x8(%rbp)
1146: 89 45 fc mov %eax,-0x4(%rbp)
1149: 8b 45 fc mov -0x4(%rbp),%eax
114c: 5d pop %rbp
114d: c3 retq
114e: 66 90 xchg %ax,%ax

相关阅读