前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用Java实现JVM第九章《本地方法调用》

用Java实现JVM第九章《本地方法调用》

原创
作者头像
小傅哥
修改2020-01-20 09:44:00
5360
修改2020-01-20 09:44:00
举报

小傅哥 | https://bugstack.cn 沉淀、分享、成长,专注于原创专题案例,以最易学习编程的方式分享知识,让自己和他人都能有所收获。目前已完成的专题有;Netty4.x实战专题案例、用Java实现JVM、基于JavaAgent的全链路监控、手写RPC框架、架构设计专题案例、源码分析等。 你用剑🗡、我用刀🔪,好的代码都很烧,望你不吝出招!

一、案例介绍

本章主要介绍用java实现一些本地方法类库,并初始化本地方法,之后通过反射命令来调用本地方法。

Java虚拟机和Java类库一起构成了Java运行时环境。Java类库主要用Java语言编写,一些无法用Java语言实现的方法则使用本地语言编写,这额方法叫作本地方法。

OpenJDK类库中的本地方法是用JNI(Java Native Interface)编写的,但是要让虚拟机支持JNI规范还需要大量工作。

二、环境准备

  1. jdk 1.8.0
  2. IntelliJ IDEA Community Edition 2018.3.1 x64

三、配置信息

  1. 调试配置
1. 配置位置:Run/Debug Configurations -> program arguments
2. 配置内容:-Xjre "C:\Program Files\Java\jdk1.8.0\_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-09\target\test-classes\org\itstack\demo\test\HelloWorld

四、代码示例

itstack-demo-jvm-09

├── pom.xml

└── src

    └── main

    │    └── java

    │        └── org.itstack.demo.jvm

    │             ├── \_native

    │             │   ├── java

    │             │   │   ├── \_Class.java

    │             │   │   ├── \_Double.java

    │             │   │   ├── \_Float.java

    │             │   │   ├── \_Object.java

    │             │   │   ├── \_String.java

    │             │   │   └── \_System.java    

    │             │   └── sun    

    │             ├── NativeMethod.java

    │             └── Registry.java    

    │             ├── classfile

    │             │   ├── attributes   

    │             │   ├── constantpool 

    │             │   ├── ClassFile.java

    │             │   ├── ClassReader.java

    │             │   └── MemberInfo.java   

    │             ├── classpath

    │             │   ├── impl

    │             │   │   ├── CompositeEntry.java

    │             │   │   ├── DirEntry.java 

    │             │   │   ├── WildcardEntry.java 

    │             │   │   └── ZipEntry.java    

    │             │   ├── Classpath.java

    │             │   └── Entry.java   

    │             ├── classpath

    │             │   ├── base

    │             │   │   ├── BytecodeReader.java

    │             │   │   ├── ClassInitLogic.java

    │             │   │   ├── Instruction.java

    │             │   │   ├── InstructionBranch.java

    │             │   │   ├── InstructionIndex8.java

    │             │   │   ├── InstructionIndex16.java

    │             │   │   ├── InstructionNoOperands.java    

    │             │   │   └── MethodInvokeLogic.java

    │             │   ├── comparisons

    │             │   ├── constants

    │             │   ├── control

    │             │   ├── conversions

    │             │   ├── extended

    │             │   ├── loads

    │             │   ├── math

    │             │   ├── references

    │             │   │   ├── ANEW\_ARRAY.java

    │             │   │   ├── ARRAY\_LENGTH.java

    │             │   │   ├── CHECK\_CAST.java

    │             │   │   ├── GET\_FIELD.java

    │             │   │   ├── GET\_STATIC.java

    │             │   │   ├── INSTANCE\_OF.java

    │             │   │   ├── INVOKE\_INTERFACE.java

    │             │   │   ├── INVOKE\_SPECIAL.java

    │             │   │   ├── INVOKE\_STATIC.java

    │             │   │   ├── INVOKE\_VIRTUAL.java

    │             │   │   ├── MULTI\_ANEW\_ARRAY.java

    │             │   │   ├── NEW.java

    │             │   │   ├── NEW\_ARRAY.java

    │             │   │   ├── PUT\_FIELD.java

    │             │   │   └── PUT\_STATIC.java

    │             │   ├── reserved

    │             │   │   └── INVOKE\_NATIVE.java    

    │             │   ├── stack

    │             │   ├── store

    │             │   │   └── xastore

    │             │   │       ├── AASTORE.java    

    │             │   │       ├── BASTORE.java    

    │             │   │       ├── CASTORE.java    

    │             │   │       ├── DASTORE.java

    │             │   │       ├── FASTORE.java

    │             │   │       ├── IASTORE.java

    │             │   │       ├── LASTORE.java    

    │             │   │       └── SASTORE.java        

    │             │   └── Factory   

    │             ├── rtda

    │             │   ├── heap

    │             │   │   ├── constantpool

    │             │   │   ├── methodarea

    │             │   │   │   ├── Class.java    

    │             │   │   │   ├── ClassMember.java  

    │             │   │   │   ├── Field.java    

    │             │   │   │   ├── Method.java 

    │             │   │   │   ├── MethodDescriptor.java 

    │             │   │   │   ├── MethodDescriptorParser.java 

    │             │   │   │   ├── MethodLookup.java     

    │             │   │   │   ├── Object.java   

    │             │   │   │   ├── Slots.java   

    │             │   │   │   └── StringPool.java    

    │             │   │   └── ClassLoader.java  

    │             │   ├── Frame.java

    │             │   ├── JvmStack.java

    │             │   ├── LocalVars.java

    │             │   ├── OperandStack.java

    │             │   ├── Slot.java 

    │             │   └── Thread.java

    │             ├── Cmd.java

    │             ├── Interpret.java    

    │             └── Main.java

    └── test

         └── java

             └── org.itstack.demo.test

                 └── HelloWorld.java

代码片段

_Class.java

package org.itstack.demo.jvm.\_native.java;



import org.itstack.demo.jvm.\_native.NativeMethod;

import org.itstack.demo.jvm.\_native.Registry;

import org.itstack.demo.jvm.rtda.Frame;

import org.itstack.demo.jvm.rtda.LocalVars;

import org.itstack.demo.jvm.rtda.OperandStack;

import org.itstack.demo.jvm.rtda.heap.ClassLoader;

import org.itstack.demo.jvm.rtda.heap.methodarea.Class;

import org.itstack.demo.jvm.rtda.heap.methodarea.Object;

import org.itstack.demo.jvm.rtda.heap.methodarea.StringPool;



/\*\*

 \* https://bugstack.cn/

 \* create by fuzhengwei on 2019/4/30

 \*/

public class \_Class {



    private final String jlClass = "java/lang/Class";



    public \_Class() {

        Registry.register(jlClass, "getPrimitiveClass", "(Ljava/lang/String;)Ljava/lang/Class;", new NativeMethod(this, "getPrimitiveClass"));

        Registry.register(jlClass, "getName0", "()Ljava/lang/String;", new NativeMethod(this, "getName0"));

        Registry.register(jlClass, "desiredAssertionStatus0", "(Ljava/lang/Class;)Z", new NativeMethod(this, "desiredAssertionStatus0"));

        Registry.register(jlClass, "registerNatives", "()V", new NativeMethod(this, "registerNatives"));

    }



    public void registerNatives(Frame frame) {

        // do nothing

    }



    public void getPrimitiveClass(Frame frame) {

        Object nameObj = frame.localVars().getRef(0);

        String name = StringPool.goString(nameObj);



        ClassLoader loader = frame.method().clazz().loader();

        Object jClass = loader.loadClass(name).jClass();



        frame.operandStack().pushRef(jClass);

    }



    public void getName0(Frame frame) {

        Object thiz = frame.localVars().getThis();

        Class clazz = (Class) thiz.extra();



        String name = "虚拟机本地方法getName0获取类名:" + clazz.javaName();

        Object nameObj = StringPool.jString(clazz.loader(), name);



        frame.operandStack().pushRef(nameObj);

    }



    public void desiredAssertionStatus0(Frame frame) {

        frame.operandStack().pushBoolean(false);

    }



    public void isInterface(Frame frame) {

        LocalVars vars = frame.localVars();

        Object thiz = vars.getThis();

        Class clazz = (Class) thiz.extra();



        OperandStack stack = frame.operandStack();

        stack.pushBoolean(clazz.isInterface());

    }



    public void isPrimitive(Frame frame) {

        LocalVars vars = frame.localVars();

        Object thiz = vars.getThis();

        Class clazz = (Class) thiz.extra();



        OperandStack stack = frame.operandStack();

        stack.pushBoolean(clazz.IsPrimitive());

    }



}

_System.java

package org.itstack.demo.jvm.\_native.java;



import org.itstack.demo.jvm.\_native.NativeMethod;

import org.itstack.demo.jvm.\_native.Registry;

import org.itstack.demo.jvm.rtda.Frame;

import org.itstack.demo.jvm.rtda.LocalVars;

import org.itstack.demo.jvm.rtda.heap.methodarea.Class;

import org.itstack.demo.jvm.rtda.heap.methodarea.Object;



/\*\*

 \* https://bugstack.cn/

 \* create by fuzhengwei on 2019/4/30

 \*/

public class \_System {



    private final String jlSystem = "java/lang/System";



    public \_System() {

        Registry.register(jlSystem, "arraycopy", "()Ljava/lang/String;", new NativeMethod(this, "arraycopy"));

        Registry.register(jlSystem,"registerNatives", "()V",new NativeMethod(this,"registerNatives"));

    }



    public void registerNatives(Frame frame) {

        // do nothing

    }

    

    public void arraycopy(Frame frame) {

        LocalVars vars = frame.localVars();

        Object src = vars.getRef(0);

        int srcPos = vars.getInt(1);

        Object dest = vars.getRef(2);

        int destPos = vars.getInt(4);

        int length = vars.getInt(4);



        if (null == src || dest == null) {

            throw new NullPointerException();

        }



        if (!checkArrayCopy(src, dest)) {

            throw new ArrayStoreException();

        }



        if (srcPos < 0 || destPos < 0 || length < 0 ||

                srcPos + length > src.arrayLength() ||

                destPos + length > dest.arrayLength()) {

            throw new IndexOutOfBoundsException();

        }



        System.arraycopy(src, srcPos, dest, destPos, length);



        //todo 待完善



    }



    public boolean checkArrayCopy(Object src, Object dest) {

        Class srcClass = src.clazz();

        Class destClass = dest.clazz();



        if (!srcClass.isArray() || !destClass.isArray()) {

            return false;

        }



        if (srcClass.componentClass().IsPrimitive() || destClass.componentClass().IsPrimitive()) {

            return srcClass == destClass;

        }



        return true;



    }



}

NativeMethod.java

package org.itstack.demo.jvm.\_native;



import org.itstack.demo.jvm.rtda.Frame;



import java.lang.reflect.Method;



/\*\*

 \* https://bugstack.cn/

 \* create by fuzhengwei on 2019/4/30

 \*/

public class NativeMethod {



    private String methodName;

    private Object obj;



    public NativeMethod(Object obj, String methodName) {

        this.methodName = methodName;

        this.obj = obj;

    }



    public void invoke(Frame frame) {

        try {

            Method method = obj.getClass().getMethod(methodName, frame.getClass());

            method.invoke(obj, frame);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }



}

Registry.java

package org.itstack.demo.jvm.\_native;



import org.itstack.demo.jvm.\_native.java.\*;



import java.util.HashMap;

import java.util.Map;



/\*\*

 \* https://bugstack.cn/

 \* create by fuzhengwei on 2019/4/30

 \*/

public class Registry {



    private static Map<String, NativeMethod> registry = new HashMap<>();



    //初始化本地方法

    public static void initNative() {

        new \_Class();

        new \_Double();

        new \_Float();

        new \_Object();

        new \_String();

        new \_System();

    }



    public static void register(String className, String methodName, String methodDescriptor, NativeMethod method) {

        String key = className + "~" + methodName + "~" + methodDescriptor;

        registry.put(key, method);

    }



    public static NativeMethod findNativeMethod(String className, String methodName, String methodDescriptor) {

        String key = className + "~" + methodName + "~" + methodDescriptor;

        return registry.get(key);

    }



}

INVOKE_NATIVE.java

package org.itstack.demo.jvm.instructions.reserved;



import org.itstack.demo.jvm.\_native.NativeMethod;

import org.itstack.demo.jvm.\_native.Registry;

import org.itstack.demo.jvm.instructions.base.InstructionNoOperands;

import org.itstack.demo.jvm.rtda.Frame;

import org.itstack.demo.jvm.rtda.heap.methodarea.Method;



/\*\*

 \* https://bugstack.cn/

 \* create by fuzhengwei on 2019/5/2

 \*/

public class INVOKE\_NATIVE extends InstructionNoOperands {



    @Override

    public void execute(Frame frame) {

        Method method = frame.method();

        String className = method.clazz().name();

        String methodName = method.name();

        String methodDescriptor = method.descriptor();



        NativeMethod nativeMethod = Registry.findNativeMethod(className, methodName, methodDescriptor);

        if (null == nativeMethod) {

            String methodInfo = className + "." + methodName + methodDescriptor;

            throw new UnsatisfiedLinkError(methodInfo);

        }



        nativeMethod.invoke(frame);



    }



}

ClassLoader.java

package org.itstack.demo.jvm.rtda.heap;



import org.itstack.demo.jvm.classfile.ClassFile;

import org.itstack.demo.jvm.classpath.Classpath;

import org.itstack.demo.jvm.rtda.heap.constantpool.AccessFlags;

import org.itstack.demo.jvm.rtda.heap.methodarea.\*;

import org.itstack.demo.jvm.rtda.heap.constantpool.RunTimeConstantPool;

import org.itstack.demo.jvm.rtda.heap.methodarea.Class;

import org.itstack.demo.jvm.rtda.heap.methodarea.Object;



import java.util.HashMap;

import java.util.Map;



/\*

class names:

    - primitive types: boolean, byte, int ...

    - primitive arrays: [Z, [B, [I ...

    - non-array classes: java/lang/Object ...

    - array classes: [Ljava/lang/Object; ...

\*/

public class ClassLoader {



    private Classpath classpath;

    private Map<String, Class> classMap;



    public ClassLoader(Classpath classpath) {

        this.classpath = classpath;

        this.classMap = new HashMap<>();



        this.loadBasicClasses();

        this.loadPrimitiveClasses();

    }



    private void loadBasicClasses() {

        Class jlClassClass = this.loadClass("java/lang/Class");

        for (Map.Entry<String, Class> entry : this.classMap.entrySet()) {

            Class clazz = entry.getValue();

            if (clazz.jClass == null) {

                clazz.jClass = jlClassClass.newObject();

                clazz.jClass.extra = clazz;

            }

        }

    }



    private void loadPrimitiveClasses() {

        for (Map.Entry<String, String> entry : ClassNameHelper.primitiveTypes.entrySet()) {

            loadPrimitiveClass(entry.getKey());

        }

    }



    private void loadPrimitiveClass(String className) {

        Class clazz = new Class(AccessFlags.ACC\_PUBLIC,

                className,

                this,

                true);

        clazz.jClass = this.classMap.get("java/lang/Class").newObject();

        clazz.jClass.extra = clazz;

        this.classMap.put(className, clazz);

    }



    public Class loadClass(String className) {

        Class clazz = classMap.get(className);

        if (null != clazz) return clazz;



        //'['数组标识

        if (className.getBytes()[0] == '[') {

            clazz = loadArrayClass(className);

        } else {

            clazz = loadNonArrayClass(className);

        }



        Class jlClazz = this.classMap.get("java/lang/Class");

        if (null != jlClazz && null != clazz) {

            clazz.jClass = jlClazz.newObject();

            clazz.jClass.extra = clazz;

        }



        return clazz;

    }



    private Class loadArrayClass(String className) {

        Class clazz = new Class(AccessFlags.ACC\_PUBLIC,

                className,

                this,

                true,

                this.loadClass("java/lang/Object"),

                new Class[]{

                        this.loadClass("java/lang/Cloneable"),

                        this.loadClass("java/io/Serializable")});

        this.classMap.put(className, clazz);

        return clazz;

    }



    private Class loadNonArrayClass(String className) {

        try {

            byte[] data = this.classpath.readClass(className);

            if (null == data) {

                throw new ClassNotFoundException(className);

            }

            Class clazz = defineClass(data);

            link(clazz);

            return clazz;

        } catch (Exception e) {

            e.printStackTrace();

            return null;

        }

    }



    private void link(Class clazz) {

        verify(clazz);

        prepare(clazz);

    }



    private void prepare(Class clazz) {

        calcInstanceFieldSlotIds(clazz);

        calcStaticFieldSlotIds(clazz);

        allocAndInitStaticVars(clazz);

    }



    private void allocAndInitStaticVars(Class clazz) {

        clazz.staticVars = new Slots(clazz.staticSlotCount);

        for (Field field : clazz.fields) {

            if (field.isStatic() && field.isFinal()) {

                initStaticFinalVar(clazz, field);

            }

        }

    }



    private void initStaticFinalVar(Class clazz, Field field) {

        Slots staticVars = clazz.staticVars;

        RunTimeConstantPool constantPool = clazz.runTimeConstantPool;

        int cpIdx = field.constValueIndex();

        int slotId = field.slotId();



        if (cpIdx > 0) {

            switch (field.descriptor()) {

                case "Z":

                case "B":

                case "C":

                case "S":

                case "I":

                    java.lang.Object val = constantPool.getConstants(cpIdx);

                    staticVars.setInt(slotId, (Integer) val);

                    break;

                case "J":

                    staticVars.setLong(slotId, (Long) constantPool.getConstants(cpIdx));

                    break;

                case "F":

                    staticVars.setFloat(slotId, (Float) constantPool.getConstants(cpIdx));

                    break;

                case "D":

                    staticVars.setDouble(slotId, (Double) constantPool.getConstants(cpIdx));

                    break;

                case "Ljava/lang/String;":

                    String goStr = (String) constantPool.getConstants(cpIdx);

                    Object jStr = StringPool.jString(clazz.loader(), goStr);

                    staticVars.setRef(slotId, jStr);

                    break;

            }

        }



    }



    private void calcStaticFieldSlotIds(Class clazz) {

        int slotId = 0;

        for (Field field : clazz.fields) {

            if (field.isStatic()) {

                field.slotId = slotId;

                slotId++;

                if (field.isLongOrDouble()) {

                    slotId++;

                }

            }

        }

        clazz.staticSlotCount = slotId;

    }



    private void calcInstanceFieldSlotIds(Class clazz) {

        int slotId = 0;

        if (clazz.superClass != null) {

            slotId = clazz.superClass.instanceSlotCount;

        }

        for (Field field : clazz.fields) {

            if (!field.isStatic()) {

                field.slotId = slotId;

                slotId++;

                if (field.isLongOrDouble()) {

                    slotId++;

                }

            }

        }

        clazz.instanceSlotCount = slotId;

    }



    private void verify(Class clazz) {

        // 校验字节码,尚未实现

    }



    private Class defineClass(byte[] data) throws Exception {

        Class clazz = parseClass(data);

        clazz.loader = this;

        resolveSuperClass(clazz);

        resolveInterfaces(clazz);

        this.classMap.put(clazz.name, clazz);

        return clazz;

    }



    private void resolveInterfaces(Class clazz) throws Exception {

        int interfaceCount = clazz.interfaceNames.length;

        if (interfaceCount > 0) {

            clazz.interfaces = new Class[interfaceCount];

            for (int i = 0; i < interfaceCount; i++) {

                clazz.interfaces[i] = clazz.loader.loadClass(clazz.interfaceNames[i]);

            }

        }

    }



    private void resolveSuperClass(Class clazz) throws Exception {

        if (!clazz.name.equals("java/lang/Object")) {

            clazz.superClass = clazz.loader.loadClass(clazz.superClassName);

        }

    }



    private Class parseClass(byte[] data) {

        ClassFile classFile = new ClassFile(data);

        return new Class(classFile);

    }





}

HelloWorld.java

package org.itstack.demo.test;



/\*\*

 \* -Xjre "C:\Program Files\Java\jdk1.8.0\_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-09\target\test-classes\org\itstack\demo\test\HelloWorld -verbose true -args 你好,java版虚拟机v1.0,欢迎你的到来。

 \*/

public class HelloWorld {



    public static void main(String[] args) {

        System.out.println(byte.class.getName()); // byte

        System.out.println(void.class.getName()); // void

        System.out.println(boolean.class.getName()); // boolean

        System.out.println(char.class.getName()); // cha

        System.out.println(short.class.getName()); // short

        System.out.println(int.class.getName()); // int

        System.out.println(long.class.getName()); // long

        System.out.println(float.class.getName()); // float

        System.out.println(double.class.getName()); // double

        System.out.println(Object.class.getName()); // java.lang.Object

        System.out.println(int[].class.getName()); // [I

        System.out.println(int[][].class.getName()); // [[I

        System.out.println(Object[].class.getName()); // [Ljava.lang.Object;

        System.out.println(Object[][].class.getName()); // [[Ljava.lang.Object;

    }



}

五、测试结果

虚拟机本地方法getName0获取类名:byte

虚拟机本地方法getName0获取类名:void

虚拟机本地方法getName0获取类名:boolean

虚拟机本地方法getName0获取类名:cha

虚拟机本地方法getName0获取类名:short

虚拟机本地方法getName0获取类名:int

虚拟机本地方法getName0获取类名:long

虚拟机本地方法getName0获取类名:float

虚拟机本地方法getName0获取类名:double

虚拟机本地方法getName0获取类名:java.lang.Object

虚拟机本地方法getName0获取类名:[I

虚拟机本地方法getName0获取类名:[[I

虚拟机本地方法getName0获取类名:[Ljava.lang.Object;

虚拟机本地方法getName0获取类名:[[Ljava.lang.Object;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、案例介绍
  • 二、环境准备
  • 三、配置信息
  • 四、代码示例
  • 五、测试结果
相关产品与服务
应用性能监控
应用性能监控(Application Performance Management,APM)是一款应用性能管理平台,基于实时多语言应用探针全量采集技术,为您提供分布式性能分析和故障自检能力。APM 协助您在复杂的业务系统里快速定位性能问题,降低 MTTR(平均故障恢复时间),实时了解并追踪应用性能,提升用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档