zoukankan      html  css  js  c++  java
  • 带你掌握不同平台下,探索JDK源码所需的native方法

    摘要:要探索JDK的核心底层源码,那必须掌握native用法。文章中会以“获取系统的默认时区”为例,介绍说明如何查看native对应方法的源码。

    本文分享自华为云社区《要探索JDK的核心底层源码,那必须掌握native用法》,作者: 小虚竹 。

    场景

    有探索欲的同学,应该会跟我一样,在看JDK源码时,跟到最后,会出现native方法,类似下面这个方法

     /**
         * Gets the platform defined TimeZone ID.
         **/
        private static native String getSystemTimeZoneID(String javaHome);

    看到这个native ,说明已经挖到核心了,到了这一步,还是不清楚是怎么获取系统的默认时区的,那怎么办,JDK代码只能跟到这里。

    转战OpenJDK,源码下载方式:https://gitee.com/mirrors/openjdk

    什么是native

    native是一个计算机函数,一个Native Method就是一个Java调用非Java代码的接口。方法的实现由非Java语言实现,比如C或C++。

    native的源码怎么看呢

    以**private static native String getSystemTimeZoneID(String javaHome)**为例

    getSystemTimeZoneID方法所在的package java.util.TimeZone;

    如图所示,找到TimeZone.c下的getSystemTimeZoneID方法

    /*
     * Gets the platform defined TimeZone ID
     */
    JNIEXPORT jstring JNICALL
    Java_java_util_TimeZone_getSystemTimeZoneID(JNIEnv *env, jclass ign,
                                                jstring java_home, jstring country)
    {
        const char *cname;
        const char *java_home_dir;
        char *javaTZ;
    
        if (java_home == NULL)
            return NULL;
    
        java_home_dir = JNU_GetStringPlatformChars(env, java_home, 0);
        if (java_home_dir == NULL)
            return NULL;
    
        if (country != NULL) {
            cname = JNU_GetStringPlatformChars(env, country, 0);
            /* ignore error cases for cname */
        } else {
            cname = NULL;
        }
    
        /*
         * Invoke platform dependent mapping function
         */
        javaTZ = findJavaTZ_md(java_home_dir, cname);
    
        free((void *)java_home_dir);
        if (cname != NULL) {
            free((void *)cname);
        }
    
        if (javaTZ != NULL) {
            jstring jstrJavaTZ = JNU_NewStringPlatform(env, javaTZ);
            free((void *)javaTZ);
            return jstrJavaTZ;
        }
        return NULL;
    }

    重点:调用不同平台相关的映射函数

    /*
         * Invoke platform dependent mapping function
         */
        javaTZ = findJavaTZ_md(java_home_dir, cname);

    去查找findJavaTZ_md方法时,发现存在分别在solaris和windows两个目录下。

    查了下这两个目录的差别:

    因为OpenJDK里,Java标准库和部分工具的源码repo(jdk目录)里,BSD和Linux的平台相关源码都是在solaris目录里的。
    原本Sun JDK的源码里平台相关的目录就是从solaris和windows这两个目录开始的,后来Unix系的平台相关代码全都放在solaris目录下了,共用大部分代码。
    
    作者:RednaxelaFX
    链接:https://www.zhihu.com/question/58982441/answer/170264788
    来源:知乎

    简单的理解就是:

    • window系统下,使用windows目录下编译的JDK代码
    • unix系的平台下,使用solaris目录下编译的JDK代码

    了解不同系统下findJavaTZ_md方法执行

    windows系统

    /*
     * Detects the platform time zone which maps to a Java time zone ID.
     */
    char *findJavaTZ_md(const char *java_home_dir, const char *country)
    {
        char winZoneName[MAX_ZONE_CHAR];
        char winMapID[MAX_MAPID_LENGTH];
        char *std_timezone = NULL;
        int  result;
    
        winMapID[0] = 0;
        result = getWinTimeZone(winZoneName, winMapID);
    
        if (result != VALUE_UNKNOWN) {
            if (result == VALUE_GMTOFFSET) {
                std_timezone = _strdup(winZoneName);
            } else {
                std_timezone = matchJavaTZ(java_home_dir, result,
                                           winZoneName, winMapID, country);
            }
        }
    
        return std_timezone;
    }
    注释写得很清楚,获取“Time Zones”注册表中的当前时区
    
    /*
     * Gets the current time zone entry in the "Time Zones" registry.
     */
    static int getWinTimeZone(char *winZoneName, char *winMapID)
    {
    ...
    }

    时区的设置方式:

    那时区上的选择值是从哪取到的,上面有说了,是在注册表中取值

    打开注册表 :Regedit–>

    计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\

    unix系的平台

    findJavaTz_md()方法的注释上写得很清楚了:将平台时区ID映射为Java时区ID

    /*
     * findJavaTZ_md() maps platform time zone ID to Java time zone ID
     * using <java_home>/lib/tzmappings. If the TZ value is not found, it
     * trys some libc implementation dependent mappings. If it still
     * can't map to a Java time zone ID, it falls back to the GMT+/-hh:mm
     * form. `country', which can be null, is not used for UNIX platforms.
     */
    /*ARGSUSED1*/
    char *
    findJavaTZ_md(const char *java_home_dir, const char *country)
    {
        char *tz;
        char *javatz = NULL;
        char *freetz = NULL;
    
        tz = getenv("TZ");
    
    #ifdef __linux__
        if (tz == NULL) {
    #else
    #ifdef __solaris__
        if (tz == NULL || *tz == '\0') {
    #endif
    #endif
            tz = getPlatformTimeZoneID();
            freetz = tz;
        }
    
        /*
         * Remove any preceding ':'
         */
        if (tz != NULL && *tz == ':') {
            tz++;
        }
    
    #ifdef __solaris__
        if (strcmp(tz, "localtime") == 0) {
            tz = getSolarisDefaultZoneID();
            freetz = tz;
        }
    #endif
    
        if (tz != NULL) {
    #ifdef __linux__
            /*
             * Ignore "posix/" prefix.
             */
            if (strncmp(tz, "posix/", 6) == 0) {
                tz += 6;
            }
    #endif
            javatz = strdup(tz);
            if (freetz != NULL) {
                free((void *) freetz);
            }
        }
        return javatz;
    }

    步骤:

    1、使用< Java home>/lib/tzmappings,。如果没有找到"TZ"变量,就进行第2步

    2、 tz = getPlatformTimeZoneID(); 执行Linux特定的映射,如果找到,返回一个时区ID,否则返回null

    【Linux】Centos7修改系统时区timezone方式:

    timedatectl

    修改时区

    timedatectl  set-timezone Asia/Shanghai

    3、对比/etc/localtime与"/usr/share/zoneinfo目录下的文件,如果一致,就返回时区ID,没有则到第4步

    4、返回到GMT

     

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    array_values — 返回数组中所有的值
    array_unshift — 在数组开头插入一个或多个单元
    array_unique — 移除数组中重复的值
    array_uintersect — 计算数组的交集,用回调函数比较数据
    array_uintersect_uassoc — 带索引检查计算数组的交集,用单独的回调函数比较数据和索引
    职场中的起跑线上,从赢在一个办公邮箱开始
    可终身使用的会员邮箱靓号,到底有多酷?
    商务业务人员,用什么邮箱更能获得认可?
    电子邮箱有哪些类型,2020什么邮箱最火爆?
    外贸邮箱用哪个比较好?企业域名邮箱注册哪个好?
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/15420547.html
Copyright © 2011-2022 走看看