Java是解释执行吗?

Java是解释执行吗?

并不完全是,我们编写的Java代码,首先通过javac编译成字节码,然后在运行时被JVM内嵌的解释器将字节码逐行解释为机器码。但是每次都逐行解释效率非常低,所以JVM提供了JIT编译器,用于将热点代码直接编译成机器码并缓存,后续直接调用而不需要再次编译或解释。那么对于部分热点代码就是编译执行了,剩下的非热点代码依旧还是解释执行。

动态编译–JIT编译器

原理

JIT工作原理

对于Java代码,刚开始都是被编译器编译成字节码文件,然后字节码文件会被交给JVM解释执行,而JIT编译技术会将运行频率很高的字节码直接编译成机器指令以提高性能。

server模式和client模式

JIT编译器在运行时有两种模式,server模式相对于client模式来说,启动时速度较慢,但是一旦运行起来,性能会有很大提升。原因是client模式使用的是C1轻量级编译器,而server模式启动的是C2重量级编译器,C2比C1编译的更加彻底,所以启动较慢,但是启动后性能更好。

JS深入-原型和class

原型

ES5中通过构造函数创建对象

Person就是一个构造函数,使用new创建一个对象实例。

1
2
3
4
5
6
7
function Person() {

}

var person = new Person();
person.name = 'gao';
console.log(person.name);

prototype

每个函数都有一个prototype属性,该属性指向了该函数的原型。每一个js对象在创建的时候都会关联一个原型,对象从原型继承属性和方法。

proto

每个js对象都有一个属性proto,该属性指向了其关联的原型。

1
2
3
person.__proto__ === Person.prototype === Object.getPrototypeOf(person)

构造函数通过prototype属性关联原型,而对象通过__proto__属性关联原型

该属性一般不直接使用,一般通过Object.getPrototypeOf(person)来获取。

constructor

原型通过constructor属性关联构造函数

1
Person === Person.prototype.constructor

原型链

如果实例中找不到该属性,就会查找该实例关联的原型中的属性,如果原型中找不到,则会查找原型的原型,直到找到Object构造函数的原型。

1
2
3
1. 查找person中属性
2. 查找Person.prototype中属性
3. 查找Object.prototype中属性

ES6中的class

ES6中通过class创建对象

class的类型就是函数,类本身就指向构造函数。类的所有方法都定义在prototype上面,在类的实例上调用方法,就是调用原型上的方法。

但是类内部定义的方法,是不能枚举的。

1
2
3
4
5
6
7
8
9
10
11
12
13
Person === Person.prototype.constructor

class Person {
constructor(name) {
this.name = name;
}

print() {
console.log(this.name)
}
}

new Person('gao zhen').print();

constructor方法默认返回实例对象this。

LeetCode-268缺失数字

给定一个包含 0, 1, 2, …, n 中 n 个数的序列,找出 0 .. n 中没有出现在序列中的那个数。

示例 1
1
2
输入: [3,0,1]
输出: 2
示例 2
1
2
输入: [9,6,4,2,3,5,7,0,1]
输出: 8
说明

你的算法应具有线性时间复杂度。你能否仅使用额外常数空间来实现?

解题思路
1
n ^ a ^ a = n
解答

JS深入-作用域和上下文栈

作用域

作用域确定了当前执行代码对变量的访问权限。

静态作用域

静态作用域在函数定义的时候就决定了,如果当前作用域没有,会查找上一层代码。而动态作用域是在运行的时候确定的,如果当前作用域没有,会查找调用函数的作用域。

js采用的是静态作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f();
}
checkscope();
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
checkscope()();

上面两段代码都会打印 local scope,因为函数的作用域基于函数创建的位置,js函数的执行用到了作用域链,这个作用域链是在函数定义的时候创建的。

上下文栈

js引擎并非一行一行的分析和执行,而是一段一段执行。当执行一段代码时,会进行变量提升、函数提升等准备操作。

js中可执行代码类型有 全局代码、函数代码、eval代码。

js维护一个执行上下文栈,最先入栈的是全局代码上下文,当执行一个函数时,就会创建一个执行上下文入栈,当函数执行完毕,就会出栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function foo() {

console.log('foo1');

}

foo(); // foo2

function foo() {

console.log('foo2');

}

foo(); // foo2

对于每个执行上下文都有三个重要属性: 变量对象、作用域链、this

FastThreadLocal快在哪

ThreadLocal原理

ThreadLocal本质上就是在Thread对象中维护了一个Map对象,Map的key为ThreadLocal自身,value为要存的值。将数据保存在Thread中而不是保存在ThreadLocal中,这一设计使得当线程对象销毁时,在该线程的所有变量都会随之销毁。以ThreadLocal对象为key,使得一个Thread对象可以同时被多个ThreadLocal对象使用,每个ThreadLocal对应一个值,保存在Thread对象的Map中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 该Map在Thread对象中,由ThreadLocal对象维护
ThreadLocal.ThreadLocalMap threadLocals = null;

public void set(T value) {
Thread t = Thread.currentThread();
// 获取该线程的Map
ThreadLocalMap map = getMap(t);
// 将当前ThreadLocal和值存入Map
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}

FastThreadLocal优化点

由于ThreadLocal使用的是hash map数据结构,其查找需要通过hash值计算索引,再通过索引查找值,其速度比数组结构要慢。所以Netty提供了一种数组结构的ThreadLocal,也就是FastThreadLocal。其实现原理是在Thread对象中维护了一个InternalThreadLocalMap对象,该InternalThreadLocalMap对象内部维护了一个数组,数组中保存要存的值,不保存对应的FastThreadLocal对象,而是每个FastThreadLocal自己维护该值在数组中对应的索引。一个FastThreadLocal所保存值对应的索引在其创建的时候已经确定,其所保存的所有Thread都应用该索引位置。

1
2
3
4
5
6
// 创建FastThreadLocal,确定其所对应的数组索引。
// 该值是静态原子类型的,所有ThreadLocal对象共享,所以不会出现索引冲突值被覆盖等问题。
static final AtomicInteger nextIndex = new AtomicInteger();
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}

Netty编解码器

ByteToMessageCodec

字节转换成消息对象的编解码器。实现了ChannelInboundHandlerAdapter和ChannelOutboundHandler,说明无论是对外还是对内都会经过该Handler。ByteToMessageCodec内部维护encoder和decoder两个对象,负责编码和解码。对于编解码器只需要处理read和write两种请求,当发生read时会将请求传递给decoder对象去解码,当发生write时会将请求传递给encoder对象编码。

1
2
3
4
5
6
7
8
9
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
decoder.channelRead(ctx, msg);
}

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
encoder.write(ctx, msg, promise);
}

再来看看encoder和decoder这两个对象,encoder是一个MessageToByteEncoder消息对象转换成字节的编码器,decoder是一个ByteToMessageDecoder字节转换成消息对象的解码器。对外的请求会经过Encoder编码器编码成字节数据,而读取远端的数据则会经过Decoder解码成消息对象再传递给上层。所以上层在设置读取消息的Handler时必须设置在编解码器之后。

1
2
3
public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {}

public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {}

Pipeline之Handler链

ChannelPipeline采用责任链模式设计。在责任链模式中,由每个对象对其下个对象的引用而连接起来的一条链,请求在这条链上传递,知道链上某个对象处理该请求并中止传递。发送请求者并不知道链上的哪个对象处理该请求,从而使得系统可以在不影响发送端的情况下动态的重新分配责任。
Netty-pipeline责任链

DefaultChannelPipeline

Handler链的默认实现类,维护了链中的头结点和尾结点,DefaultChannelPipeline对象在构建时,就会创建头结点和尾结点对象。之后添加的Handler都会添加到两者中间,addLast会添加到尾结点的前面,addFirst会添加到头结点的后面。在添加时可以给Handler指定名称,如果名称有重复会抛出异常。
Handler分为两种,一种是实现ChannelInboundHandler接口的,一种是实现ChannelOutboundHandler接口的。ChannelInboundHandler接口主要处理从外到内的请求,将远端操作通知本地(比如注册,激活,读等),从Head结点开始传递,直到tail结点。而ChannelOutboundHandler接口主要处理从内到外的操作,将本地操作通知远端(比如连接,绑定,写)等,从tail结点开始传递,到Head结点后转由unsafe类进行真正处理。

1
2
tail = new TailContext(this); // tail实现了ChannelInboundHandler接口
head = new HeadContext(this); // head实现了ChannelOutboundHandler和ChannelInboundHandler接口

上文讲到的ServerBootstrapAcceptor类,就是一个ChannelInboundHandlerAdapter类型的Handler。所以当连接完成对上层发出通知时,通知从Head结点开始传递,到达ServerBootstrapAcceptor中处理并中止传递。

AbstractChannelHandlerContext

AbstractChannelHandlerContext是对ChannelHandler的一个包装类,Pipeline中真正的对象实际上是AbstractChannelHandlerContext。在AbstractChannelHandlerContext中维护了next、prev属性,所以这是一个双向链表结构。

1
2
3
volatile AbstractChannelHandlerContext next; // 下个结点
volatile AbstractChannelHandlerContext prev; // 上个结点
private final ChannelHandler handler; // 真正Handler对象

接下来讲到的编解码器也是根据Pipeline来实现的。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×