关于Java的十万个为什么

本实验平台主要是基于本人的MacbookPro,之后会考虑测试其他操作系统版本
基于jdk1.8,HotSpot
macOS Catalina 10.15
内存 8 GB 2133 MHz LPDDR3

$ uname -a
Darwin MacBook-Pro.local 19.0.0 Darwin Kernel Version 19.0.0: Wed Sep 25 20:18:50 PDT 2019; root:xnu-6153.11.26~2/RELEASE_X86_64 x86_64
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

为什么变量名(方法名,类名)最大长度是65535

如果你起了一个65536长度的变量名,在idea中不会提示报错,但是运行就会获得一个错误

Error:(3, 8) java: 对于常量池来说, 字符串 "aaaaaaaaaaaaaaaaaaaa..." 的 UTF8 表示过长

因为无论是字段名还是方法名或者是类名,最终在常量池中都会用一个Utf8的常量去表示,但是class文件中只给了u2个字节去表示随后的Utf8字节的长度。
而u2字节能表示最大的十进制数就是65535

FFFF = 65535

为什么方法参数最多是255

其实这个说法有问题,其实Java中最多能有255个单位(4个字节为1个单位)的参数数量,
不足4个字节也会当成4个字节,所以long和double类型会占2个单位

// 256个String
Error:(5, 25) java: 参数过多
// 255个long
Error:(5, 25) java: 参数过多
// 256个byte
Error:(5, 25) java: 参数过多
// 127个long
ok
// 255个byte,String
ok

而且静态和非静态的方法是有区别的,静态方法能比非静态方法多一个参数(可能),是因为非静态方法会有第一个参数默认指向当前实例对象,就是this
方法的参数其实会作为方法内部局部变量来使用
而Jvm中Code属性有一个max_locals属性,u2的长度来记录整个方法的局部变量Slot数量,
那么u2明明可以记录65535个Slot,为什么方法参数只能是255个呢?
让我们走进jdk

// langtools/src/share/classes/com/sun/tools/javac/jvm/Gen.java 这个是jdk javac命令相关源码
public class Gen extends JCTree.Visitor {
    ...
    // genMethod方法里会对方法参数数量进行校验
    void genMethod(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) {
        if (Code.width(types.erasure(env.enclMethod.sym.type).getParameterTypes()) + extras >
            ClassFile.MAX_PARAMETERS) {
            log.error(tree.pos(), "limit.parameters");
            nerrs++;
        }
    }
    ...
}
// langtools/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java
public class ClassFile {
    ...
    // 十六进制FF 就是 255
    public final static int MAX_PARAMETERS = 0xff;
    ...
}

既然javac不允许我们在源码中声明一个超过255参数的方法,如果直接通过字节码的技术,直接生成一个class文件,能突破这个255的限制吗
有请javassist

public class JavaassistDemo {

    public static void main(String[] args) {
        try {
            StringBuilder sb = new StringBuilder();
            sb.append("public static void testMax(");

            for (int i = 0; i < 256; i++) {
                sb.append("int a" + i);
                if (i < 255) {
                    sb.append(",");
                }
            }
            sb.append("){}");

            ClassPool cPool = new ClassPool(true);
            CtClass cClass = cPool.makeClass("TestManyWhy");
            cClass.addMethod(CtNewMethod.make(sb.toString(), cClass));
            cClass.addMethod(CtNewMethod.make("public static void main(String[] args) {\n" +
                    "        System.out.println(\"testmain\");\n" +
                    "    }", cClass));
            cClass.writeFile("/Users/junjiexun/IdeaProjects/jvmtest/target/classes/jvmtest");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

先通过javap查看下class文件,可以看到testMax是生成了

$ javap TestManyWhy
public class TestManyWhy {
  public static void testMax(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
  public static void main(java.lang.String[]);
  public TestManyWhy();
}

运行试试

$ java TestManyWhy
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.ClassFormatError: Too many arguments in method signature in class file TestManyWhy
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

还是获得了一个错误,证明jvm在运行的时候也会对参数长度进行校验
通过错误提示,再去扒openjdk的源码

// hotspot/src/share/vm/classfile/classFileParser.cpp
#define MAX_ARGS_SIZE 255
methodHandle ClassFileParser::parse_method(bool is_interface,
                                           AccessFlags *promoted_flags,
                                           TRAPS) {
  ...
  int args_size = -1;  // only used when _need_verify is true
  if (_need_verify) {
    // 这里也体现了静态和非静态,参数上限是不同的
    args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) +
                 verify_legal_method_signature(name, signature, CHECK_(nullHandle));
    if (args_size > MAX_ARGS_SIZE) {
      classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_(nullHandle));
    }
  }
  ...
}

BigDecimal可以‘精确’的表示浮点数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容