文档章节

你不知道Lambda的秘密和陷阱

路上有你0314
 路上有你0314
发布于 2019/12/22 23:26
字数 2677
阅读 12210
收藏 37

二探lambda表达式

一探Lambda:https://my.oschina.net/lt0314/blog/3144851

从例子二探lambda

传递Runnable创建Thread

java8之前

package com.baigt.learn.nolambda;
public class NoLambdaWithSecond {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
			// do some thing
            }
        });
    }
}

查看编译情况

  • 文件情况
D:\IdeaProjects\course\out\production\classes\com\baigt\learn\nolambda>ls
NoLambdaWithSecond$1.class  NoLambdaWithSecond.class

java8

package com.baigt.learn;
public class LambdaWithSecond {
    public static void main(String[] args) {
        new Thread(()->{});
    }
}

查看编译情况

  • 查看编译目录
D:\IdeaProjects\course\out\production\classes\com\baigt\learn\lambda>ls
LambdaWithSecond.class
D:\IdeaProjects\course\out\production\classes\com\baigt\learn\lambda>

在上一篇文章中,我们说过,一般情况下,lambda表达的是一个匿名类,在java8之前,编译后会替我们生成一个比如XXX$num.class的文件。那么lambda中从上边来看好像没生成这个文件啊,是不是结论是错误的?

  • 疑问?

我们的推测难道是错误的?怎么才能验证我们的结论是对的?再抛个问题,lambda因为其便捷性会被在项目中大量使用,会有什么弊端?

验证结论(一般是匿名内部类的实现),对比分析

ide反编译的文件隐藏了很多细节,java底层提供了javap命令可以显示更多的信息。那么我们就用这个命令来反编译下。

java8之前

D:\IdeaProjects\course\out\production\classes\com\baigt\learn\nolambda>javap -verbose NoLambdaWithSecond.class
Classfile /D:/IdeaProjects/course/out/production/classes/com/baigt/learn/nolambda/NoLambdaWithSecond.class
  Last modified 2019-12-22; size 611 bytes
  MD5 checksum 617cb5177a9bce206277b70044241fb9
  Compiled from "NoLambdaWithSecond.java"
public class com.baigt.learn.nolambda.NoLambdaWithSecond
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#22         // java/lang/Object."<init>":()V
   #2 = Class              #23            // java/lang/Thread
   #3 = Class              #24            // com/baigt/learn/nolambda/NoLambdaWithSecond$1
   #4 = Methodref          #3.#22         // com/baigt/learn/nolambda/NoLambdaWithSecond$1."<init>":()V
   #5 = Methodref          #2.#25         // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
   #6 = Class              #26            // com/baigt/learn/nolambda/NoLambdaWithSecond
   #7 = Class              #27            // java/lang/Object
   #8 = Utf8               InnerClasses
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               Lcom/baigt/learn/nolambda/NoLambdaWithSecond;
  #16 = Utf8               main
  #17 = Utf8               ([Ljava/lang/String;)V
  #18 = Utf8               args
  #19 = Utf8               [Ljava/lang/String;
  #20 = Utf8               SourceFile
  #21 = Utf8               NoLambdaWithSecond.java
  #22 = NameAndType        #9:#10         // "<init>":()V
  #23 = Utf8               java/lang/Thread
  #24 = Utf8               com/baigt/learn/nolambda/NoLambdaWithSecond$1
  #25 = NameAndType        #9:#28         // "<init>":(Ljava/lang/Runnable;)V
  #26 = Utf8               com/baigt/learn/nolambda/NoLambdaWithSecond
  #27 = Utf8               java/lang/Object
  #28 = Utf8               (Ljava/lang/Runnable;)V
{
  public com.baigt.learn.nolambda.NoLambdaWithSecond();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/baigt/learn/nolambda/NoLambdaWithSecond;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: new           #2                  // class java/lang/Thread
         3: dup
         4: new           #3                  // class com/baigt/learn/nolambda/NoLambdaWithSecond$1
         7: dup
         8: invokespecial #4                  // Method com/baigt/learn/nolambda/NoLambdaWithSecond$1."<init>":()V
        11: invokespecial #5                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
        14: pop
        15: return
      LineNumberTable:
        line 5: 0
        line 11: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  args   [Ljava/lang/String;
}
SourceFile: "NoLambdaWithSecond.java"
InnerClasses:
     static #3; //class com/baigt/learn/nolambda/NoLambdaWithSecond$1

D:\IdeaProjects\course\out\production\classes\com\baigt\learn\nolambda>


java8

D:\IdeaProjects\course\out\production\classes\com\baigt\learn\lambda>javap -verbose LambdaWithSecond.class
Classfile /D:/IdeaProjects/course/out/production/classes/com/baigt/learn/lambda/LambdaWithSecond.class
  Last modified 2019-12-22; size 1056 bytes
  MD5 checksum 3395121fedc061cfcd4854241ddeb1e8
  Compiled from "LambdaWithSecond.java"
public class com.baigt.learn.lambda.LambdaWithSecond
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#21         // java/lang/Object."<init>":()V
   #2 = Class              #22            // java/lang/Thread
   #3 = InvokeDynamic      #0:#27         // #0:run:()Ljava/lang/Runnable;
   #4 = Methodref          #2.#28         // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
   #5 = Class              #29            // com/baigt/learn/lambda/LambdaWithSecond
   #6 = Class              #30            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/baigt/learn/lambda/LambdaWithSecond;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               args
  #17 = Utf8               [Ljava/lang/String;
  #18 = Utf8               lambda$main$0
  #19 = Utf8               SourceFile
  #20 = Utf8               LambdaWithSecond.java
  #21 = NameAndType        #7:#8          // "<init>":()V
  #22 = Utf8               java/lang/Thread
  #23 = Utf8               BootstrapMethods
  #24 = MethodHandle       #6:#31         // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/
MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #25 = MethodType         #8             //  ()V
  #26 = MethodHandle       #6:#32         // invokestatic com/baigt/learn/lambda/LambdaWithSecond.lambda$main$0:()V
  #27 = NameAndType        #33:#34        // run:()Ljava/lang/Runnable;
  #28 = NameAndType        #7:#35         // "<init>":(Ljava/lang/Runnable;)V
  #29 = Utf8               com/baigt/learn/lambda/LambdaWithSecond
  #30 = Utf8               java/lang/Object
  #31 = Methodref          #36.#37        // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Lj
ava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #32 = Methodref          #5.#38         // com/baigt/learn/lambda/LambdaWithSecond.lambda$main$0:()V
  #33 = Utf8               run
  #34 = Utf8               ()Ljava/lang/Runnable;
  #35 = Utf8               (Ljava/lang/Runnable;)V
  #36 = Class              #39            // java/lang/invoke/LambdaMetafactory
  #37 = NameAndType        #40:#44        // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/
lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #38 = NameAndType        #18:#8         // lambda$main$0:()V
  #39 = Utf8               java/lang/invoke/LambdaMetafactory
  #40 = Utf8               metafactory
  #41 = Class              #46            // java/lang/invoke/MethodHandles$Lookup
  #42 = Utf8               Lookup
  #43 = Utf8               InnerClasses
  #44 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/
lang/invoke/CallSite;
  #45 = Class              #47            // java/lang/invoke/MethodHandles
  #46 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #47 = Utf8               java/lang/invoke/MethodHandles
{
  public com.baigt.learn.lambda.LambdaWithSecond();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/baigt/learn/lambda/LambdaWithSecond;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=1, args_size=1
         0: new           #2                  // class java/lang/Thread
         3: dup
         4: invokedynamic #3,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
         9: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
        12: pop
        13: return
      LineNumberTable:
        line 5: 0
        line 6: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  args   [Ljava/lang/String;
}
SourceFile: "LambdaWithSecond.java"
InnerClasses:
     public static final #42= #41 of #45; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
  0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodH
andle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #25 ()V
      #26 invokestatic com/baigt/learn/lambda/LambdaWithSecond.lambda$main$0:()V
      #25 ()V

D:\IdeaProjects\course\out\production\classes\com\baigt\learn\lambda>


上眼一看可能感觉是个啥,但如果你看过java8之前,会发现对比之前反编译的内容发生了很大的变化。首先是InnerClass部分,其次是多了个BootstrapMethods区域。下边是相关部分对比图

具体分析

  • Runnable部分

java8之前指向到一个class #3处,java8时则指向#3 和0处(BootStrapMethods) 这个可能还是不直观,那么我们借助工具,jclasslib来再看下。

借助工具,我们更清晰的可以得出一些结论。

  • methods 构成部分,java8出现了一个格式为“lambda$调用方法名$数量”的 一个静态方法
  • Attributes构成部分,java8出现了一个BootstrapMethods。

接下来我们看下这个方法

BootstrapMethods

调用LambdaMetafactory.metafactory方法,传入的参数包含#25,26,#25类

BootstrapMethods:
  0: #24 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodH
andle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #25 ()V
      #26 invokestatic com/baigt/learn/lambda/LambdaWithSecond.lambda$main$0:()V
      #25 ()V

下边我们从 LambdaMetafactory.metafactory入手来继续分析下

LambdaMetafactory.metafactory源码分析
  • metafactory 部分

入口

    public static CallSite metafactory(MethodHandles.Lookup caller,
                                       String invokedName,
                                       MethodType invokedType,
                                       MethodType samMethodType,
                                       MethodHandle implMethod,
                                       MethodType instantiatedMethodType)
            throws LambdaConversionException {
        AbstractValidatingLambdaMetafactory mf;
        // 创建lambda内部类元工厂
        mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                             invokedName, samMethodType,
                                             implMethod, instantiatedMethodType,
                                             false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
        mf.validateMetafactoryArgs();
        // 构建
        return mf.buildCallSite();
    }

  • InnerClassLambdaMetafactory

初始化比如类名、ClassWriter

    public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
                                       MethodType invokedType,
                                       String samMethodName,
                                       MethodType samMethodType,
                                       MethodHandle implMethod,
                                       MethodType instantiatedMethodType,
                                       boolean isSerializable,
                                       Class<?>[] markerInterfaces,
                                       MethodType[] additionalBridges)
            throws LambdaConversionException {
        super(caller, invokedType, samMethodName, samMethodType,
              implMethod, instantiatedMethodType,
              isSerializable, markerInterfaces, additionalBridges);
        implMethodClassName = implDefiningClass.getName().replace('.', '/');
        implMethodName = implInfo.getName();
        implMethodDesc = implMethodType.toMethodDescriptorString();
        implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
                ? implDefiningClass
                : implMethodType.returnType();
        constructorType = invokedType.changeReturnType(Void.TYPE);
        lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
        cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        int parameterCount = invokedType.parameterCount();
        if (parameterCount > 0) {
            argNames = new String[parameterCount];
            argDescs = new String[parameterCount];
            for (int i = 0; i < parameterCount; i++) {
                argNames[i] = "arg$" + (i + 1);
                argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
            }
        } else {
            argNames = argDescs = EMPTY_STRING_ARRAY;
        }
    }

  • java.lang.invoke.InnerClassLambdaMetafactory#buildCallSite

返回一个函数式接口的实例对象(生成相关字节码到jvm中)

    CallSite buildCallSite() throws LambdaConversionException {
    // 编织内部类
        final Class<?> innerClass = spinInnerClass();
        // 无参的话,通过构造方法返回实例,否则通过findstatic方式
        if (invokedType.parameterCount() == 0) {
            final Constructor<?>[] ctrs = AccessController.doPrivileged(
                    new PrivilegedAction<Constructor<?>[]>() {
                @Override
                public Constructor<?>[] run() {
                    Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
                    if (ctrs.length == 1) {
                        // The lambda implementing inner class constructor is private, set
                        // it accessible (by us) before creating the constant sole instance
                        ctrs[0].setAccessible(true);
                    }
                    return ctrs;
                }
                    });
            if (ctrs.length != 1) {
                throw new LambdaConversionException("Expected one lambda constructor for "
                        + innerClass.getCanonicalName() + ", got " + ctrs.length);
            }

            try {
                Object inst = ctrs[0].newInstance();
                // 这部分不细讲(大概是给CallSite赋值MethodHandle对象)
                return new ConstantCallSite(MethodHandles.constant(samBase, inst));
            }
            catch (ReflectiveOperationException e) {
                throw new LambdaConversionException("Exception instantiating lambda object", e);
            }
        } else {
            try {
                UNSAFE.ensureClassInitialized(innerClass);
                // 这部分不细讲(大概是给CallSite赋值MethodHandle对象)
                return new ConstantCallSite(
                        MethodHandles.Lookup.IMPL_LOOKUP
                             .findStatic(innerClass, NAME_FACTORY, invokedType));
            }
            catch (ReflectiveOperationException e) {
                throw new LambdaConversionException("Exception finding constructor", e);
            }
        }
    }

  • java.lang.invoke.InnerClassLambdaMetafactory#spinInnerClass

内部类编织

 private Class<?> spinInnerClass() throws LambdaConversionException {
        String[] interfaces;
        String samIntf = samBase.getName().replace('.', '/');
        boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
        if (markerInterfaces.length == 0) {
            interfaces = new String[]{samIntf};
        } else {
            // Assure no duplicate interfaces (ClassFormatError)
            Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
            itfs.add(samIntf);
            for (Class<?> markerInterface : markerInterfaces) {
                itfs.add(markerInterface.getName().replace('.', '/'));
                accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
            }
            interfaces = itfs.toArray(new String[itfs.size()]);
        }

        cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
                 lambdaClassName, null,
                 JAVA_LANG_OBJECT, interfaces);

        // Generate final fields to be filled in by constructor
        for (int i = 0; i < argDescs.length; i++) {
            FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
                                            argNames[i],
                                            argDescs[i],
                                            null, null);
            fv.visitEnd();
        }

        generateConstructor();

        if (invokedType.parameterCount() != 0) {
            generateFactory();
        }

        // Forward the SAM method
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
                                          samMethodType.toMethodDescriptorString(), null, null);
        new ForwardingMethodGenerator(mv).generate(samMethodType);

        // Forward the bridges
        if (additionalBridges != null) {
            for (MethodType mt : additionalBridges) {
                mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
                                    mt.toMethodDescriptorString(), null, null);
                new ForwardingMethodGenerator(mv).generate(mt);
            }
        }

        if (isSerializable)
            generateSerializationFriendlyMethods();
        else if (accidentallySerializable)
            generateSerializationHostileMethods();

        cw.visitEnd();

        // Define the generated class in this VM.
		// 定义好在jvm中要使用的(生成)的字节码
        final byte[] classBytes = cw.toByteArray();

        // If requested, dump out to a file for debugging purposes
        // 如果被要求,这里可以导出一个class文件作为调试使用
        if (dumper != null) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                @Override
                public Void run() {
                	// 有兴趣的可以自己点进去看下,disk写操作
                    dumper.dumpClass(lambdaClassName, classBytes);
                    return null;
                }
            }, null,
            new FilePermission("<<ALL FILES>>", "read, write"),
            // createDirectories may need it
            new PropertyPermission("user.dir", "read"));
        }
		// 通过UNSAFE本地方法将类加载到jvm中去
        return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
    }

  • java.lang.invoke.InnerClassLambdaMetafactory#dumper

通过他可以生成具体lambda文件

   // For dumping generated classes to disk, for debugging purposes
    private static final ProxyClassesDumper dumper;

    static {
    // 通过 “jdk.internal.lambda.dumpProxyClasses”属性来指定具体目录来存放生成的lambda内部类
        final String key = "jdk.internal.lambda.dumpProxyClasses";
        String path = AccessController.doPrivileged(
                new GetPropertyAction(key), null,
                new PropertyPermission(key , "read"));
        dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
    }

到这里,我们基本可以确定,lambda其实最后确定会生成匿名内部类且加载到jvm中,可以通过“jdk.internal.lambda.dumpProxyClasses”指定目录来存储到文件中

使用 jdk.internal.lambda.dumpProxyClasses

设置属性目录

package com.baigt.learn.lambda;

public class LambdaWithSecond {
    public static void main(String[] args) {
        System.setProperty("jdk.internal.lambda.dumpProxyClasses","d:\\data\\");
        new Thread(()->{});
    }
}

  • 结果
D:\data\com\baigt\learn\lambda>ls
LambdaWithSecond$$Lambda$1.class

D:\data\com\baigt\learn\lambda>javap LambdaWithSecond$$Lambda$1.class
final class com.baigt.learn.lambda.LambdaWithSecond$$Lambda$1 implements java.lang.Runnable {
  public void run();
}

D:\data\com\baigt\learn\lambda>


上边提到一个问题,项目中大量使用lambda会有什么问题?

大量使用lambda可能会有什么问题?

从上述我们了解到,lambda默认虽然不生成class文件,但是依旧会生成字节码并load到jvm中。如果使用不当,当大量的这样的数据加载到vm中,后果是可想而知的。

当大量的class加载到vm中时,java8的metaspace空间可以急剧增长,而metaspace空间,默认会自动调整阈值的,直到os内存不足,申请不到空间,会被oskill掉。感兴趣的同学可以使用cglib中的Enhancer来实践下,调用jconsole或者jmc、jvisualvm来观察元空间变化。

结语

上述是个人心得,不对之处,欢迎指正。

作者:baigt 交流群:244930845

© 著作权归作者所有

路上有你0314
粉丝 53
博文 24
码字总数 50649
作品 0
郑州
程序员
私信 提问
加载中

评论(22)

渐行0渐远
渐行0渐远
这个问题之前也有考虑过,但是没去验证,试想这个问题应该是存在的。但也不是什么大问题,除非你的 lambda 多到到处都是。
a
asurazp
吓我一跳,原来是java,不是C#的lambda
水无月青云
水无月青云
作者深入研究了,值得学习,但是我想语言设计者,应该考虑过这方面的问题,默认情况下应该会有优化吧
路上有你0314
路上有你0314 博主
jdk23后,jetty9.4.9后的优化掉了,算是之前版本的bug
exten
exten
优秀,非常正确。哈哈😄,我们都是遵循能量守恒定律的。有得必有失
xiaoyu123
xiaoyu123
限定场景下面,结论是一个问题。不限制场景,描述的问题没有实际参考意义
dwingo
dwingo
每一处invokedynamic最多只会生成一个类吧, 影响不大.
总长
总长
感谢作者,引起的思考,特意去查了一下。lambda表达式在运行时的实现方式是动态生成跟匿名内部类类似形式的类,不是真正的匿名内部类。搜索关键字是invokedynamic。运行时也存在多种编译策略。
程序人生2015
程序人生2015
作者的深入研究值得认可,但是作者的结论恕我不可苟同
msscn
msscn
危言耸听!!!
yourOnlyJack
yourOnlyJack
博主说推测的metaspace有事实证明吗
路上有你0314
路上有你0314 博主
公司这边线上见遇到过一次。全节点服务crash了
HashMap之equals与hashCode小陷阱

先以一段代码开始这篇blog。 那输出结果是什么呢?类似这样的题目总能遇到,以前不知道有什么好考的,弱智?自己动手尝试了一次,发现结果不是自己想象的那样。本篇就用来揭示HashMap的equal...

六只
2012/10/29
4.9K
7
程序员老司机都要错的 Python 陷阱与缺陷列表 - 知乎

我个人对陷阱的定义是这样的:代码看起来可以工作,但不是以你“想当然“”的方式。如果一段代码直接出错,抛出了异常,我不认为这是陷阱。比如,Python程序员应该都遇到过的“UnboundLocalE...

Python头条
2019/10/21
0
0
如何避免跳入支付宝套现、京东套现的陷阱?

在互联网日益发展的今天,支付宝、京东等在线支付软件极大的改变了我们的生活,尤其是“蚂蚁花呗、京东白条”的出现,让我们在手头紧张的时候也能购买自己心爱的物品。但是花呗、白条的钱是无...

qq5a31f758edea1
2017/12/14
0
0
(总结)python 3程序开发指南(四)控制语句与函数

1.if else 经典用法:offset = 20 if sys.platform.startswith("win") else 10,但要注意后面有时候需要加上(),防止微妙的陷阱. 2.注意while和for in循环后以及在try except模块后,都可用els...

索隆
2012/04/24
66
0
硬件大拿现场揭秘——如何开发产品更省钱

loT时代,不止是拼创意,更要拼资本! 没有资本,也想玩转烧钱的智能硬件? 机智云高级项目经理Alex为你现场揭秘——智能硬件实现零成本开发的秘密! APICloud创始人刘鑫传授独家开发秘笈从方...

物联网小能手
2016/07/20
26
0

没有更多内容

加载失败,请刷新页面

加载更多

分布式搜索引擎的架构是怎么设计的?

业内目前来说事实上的一个标准,就是分布式搜索引擎一般大家都用elasticsearch (1) es的分布式架构原理能说一下么(es是如何实现分布式的啊) 面试官心里分析 在搜索这块,lucene是最流行的搜...

Star永恒
23分钟前
97
0
集合转数组

ArrayList提供了一个将List转为数组的一个非常方便的方法toArray。toArray有两个重载的方法:   (1)list.toArray(); // 转换为Object类型的数组Object[] arr = list.toArray();for (...

那个猩猩很亮
26分钟前
78
0
使用canvas在前端添加水印

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <meta name="renderer" content="webkit"> <meta http-equiv="X-UA-Compatible" cont......

liyoungs
26分钟前
18
0
喜马拉雅音频下载工具 支持免费音频/VIP音频 文尾有彩蛋

最近流行喜马拉雅,趁你有会员,教你下载其资源 .如果没有会员可以找别人的会员获取cookie也可以.也没有别人的会员可以用此工具下载免费的音频也是不错的选择. 下载地址:https://www.90pan....

xiaogg
35分钟前
61
0
zookeeper安装(基础篇)

Zookeeper三种部署模式 1、Standalone模式,单节点部署,适合测试环境。2、伪cluster模式,单节点多实例部署,适合测试环境。3、Cluster模式,多节点集群部署,适合生产环境。 安装前准备...

丁小屁
39分钟前
35
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部