Blog

操作数栈

操作数栈 #

Java 字节码是 Java 虚拟机所使用的指令集。因此,它与 Java 虚拟机基于栈的计算模型是密不可分的。

在解释执行过程中,每当为 Java 方法分配栈桢时,Java 虚拟机往往需要开辟一块额外的空间作为操作数栈,来存放计算的操作数以及返回结果。

具体来说便是:执行每一条指令之前,Java 虚拟机要求该指令的操作数已被压入操作数栈中。在执行指令时,Java 虚拟机会将该指令所需的操作数弹出,并且将指令的结果重新压入栈中。

以加法指令 iadd 为例。假设在执行该指令前,栈顶的两个元素分别为 int 值 1 和 int 值 2,那么 iadd 指令将弹出这两个 int,并将求得的和 int 值 3 压入栈中。

由于 iadd 指令只消耗栈顶的两个元素,因此,对于离栈顶距离为 2 的元素,即图中的问号,iadd 指令并不关心它是否存在,更加不会对其进行修改。

Viewpoint #

From #

19 | Java字节码(基础篇)

pop指令

pop指令 #

pop指令则常用于舍弃调用指令的返回结果。例如在下面这段代码的 foo 方法中,我将调用静态方法 bar,但是却不用其返回值。

由于对应的 invokestatic 指令仍旧会将返回值压入 foo 方法的操作数栈中,因此 Java 虚拟机需要额外执行 pop 指令,将返回值舍弃。

public static boolean bar() {
  return false;
}
public void foo() {
  bar();
}
// foo方法对应的字节码如下:
public void foo();
  0  invokestatic FooTest.bar() : boolean [24]
  3  pop
  4  return

需要注意的是,上述两条指令只能处理非 long 或者非 double 类型的值,这是因为 long 类型或者 double 类型的值,需要占据两个栈单元。当遇到这些值时,我们需要同时复制栈顶两个单元的 dup2 指令,以及弹出栈顶两个单元的 pop2 指令。

Viewpoint #

From #

19 | Java字节码(基础篇)

dup指令

dup指令 #

dup 指令常用于复制 new 指令所生成的未经初始化的引用。例如在下面这段代码的 foo 方法中,当执行 new 指令时,Java 虚拟机将指向一块已分配的、未初始化的内存的引用压入操作数栈中。

public void foo() {
  Object o = new Object();
}
// 对应的字节码如下:
public void foo();
  0  new java.lang.Object [3]
  3  dup
  4  invokespecial java.lang.Object() [8]
  7  astore_1 [o]
  8  return

接下来,我们需要以这个引用为调用者,调用其构造器,也就是上面字节码中的 invokespecial 指令。要注意,该指令将消耗操作数栈上的元素,作为它的调用者以及参数(不过 Object 的构造器不需要参数)。

因此,我们需要利用 dup 指令复制一份 new 指令的结果,并用来调用构造器。当调用返回之后,操作数栈上仍有原本由 new 指令生成的引用,可用于接下来的操作(即偏移量为 7 的字节码)。

Viewpoint #

From #

19 | Java字节码(基础篇)

最简单的协程实现

Content #

先通过一个最简单的协程的例子,来观察协程的运作机制:

#include <stdio.h>
#include <stdlib.h>
#define STACK_SIZE 1024

typedef void(*coro_start)();

class coroutine {
public:
    long* stack_pointer;
    char* stack;

    coroutine(coro_start entry) {
        if (entry == NULL) {
            stack = NULL;
            stack_pointer = NULL;
            return;
        }

        stack = (char*)malloc(STACK_SIZE);
        char* base = stack + STACK_SIZE;
        stack_pointer = (long*) base;
        stack_pointer -= 1;
        *stack_pointer = (long) entry;
        stack_pointer -= 1;
        *stack_pointer = (long) base;
    }

    ~coroutine() {
        if (!stack)
            return;
        free(stack);
        stack = NULL;
    }
};

coroutine* co_a, * co_b;

void yield_to(coroutine* old_co, coroutine* co) {
    __asm__ (
        "movq %%rsp, %0\n\t"
        "movq %%rax, %%rsp\n\t"
        :"=m"(old_co->stack_pointer):"a"(co->stack_pointer):);
}

void start_b() {
    printf("B");
    yield_to(co_b, co_a);
    printf("D");
    yield_to(co_b, co_a);
}

int main() {
    printf("A");
    co_b = new coroutine(start_b);
    co_a = new coroutine(NULL);
    yield_to(co_a, co_b);
    printf("C");
    yield_to(co_a, co_b);
    printf("E\n");
    delete co_a;
    delete co_b;
    return 0;
}

编译运行:

...

价值投资

Content #

什么是价值投资?

价值投资的本质就是利用价格围绕价值波动这一特性,拿你手中的财富去交换更有价值的东西。

正是这种价格的波动性,为价值投资创造了获利空间。可以用一句话概括价值投资的具体操作:当价格低于价值的时候买入,持有投资标的的同时等待价格的修复,等到价格高于价值的时候再卖出,获取实际收益。如此往复,不断积累个人财富。

Viewpoint #

From #

toc:Shell

命令使用 #

bc命令中的length和scale的含义分别是什么? Commands sorted by votes

Bash #

Interactive Session和None-interactive Session 在bash脚本中获取当前脚本所在的路径 Bash中计算乘积 Bash初始文件顺序 取消指定目录下所有文件的SUID和SGID的权限 命令分组(command grouping) 判断是否生成了子shell jobs命令 协程(coproc) 外部命令与内建命令 ANSI-C Quoting case命令 使用select命令创建菜单 bash shell提供了3个可在if-then语句中使用的高级特性:

  1. 在子shell中执行命令的单括号(if-then)
  2. 用于数学表达式的双括号
  3. 用于高级字符串处理功能的双方括号

sub:Bash命令行参数 sub:获取用户输入 sub:变量(Bash) sub:输入输出重定向(Bash) sub:条件测试(Bash) sub:函数(Bash) sub:gawk sub:sed sub:谦让度(nice value) sub:at命令 sub:cron sub:信号(Signal)

zsh shell的独有特性 帐号登录审计

技巧 #

converts HH:MM:SS.sss to fractional seconds converts fractional seconds to HH:MM:SS.sss

日常维护 #

remove disabled snap packages

cook:Shell cook:systemd

man bash-builtins

Hotspot虚拟机中的安全区域

Hotspot虚拟机中的安全区域指的是什么? #

安全区域是指能够确保在某一段代码片段之中,引用关系不会发生变化,因此,在这个区域中任意地方开始垃圾收集都是安全的。我们也可以把安全区域看作被扩展拉伸了的安全点。

Viewpoint #

From #