拆卸invokedynamic

许多Java开发人员认为JDK的第七版有些令人失望。 从表面上看,仅少数语言和库扩展使它成为了发行版,即Project Coin和NIO2 。 但在幕后,该平台的第七个版本对JVM类型系统进行了最大的扩展,这是它最初发布后引入的。 添加invokedynamic指令不仅为在Java 8中实现lambda表达式奠定了基础,而且还是将动态语言转换为Java字节码格式的规则改变者。

虽然invokedynamic指令是用于在Java虚拟机上执行语言的实现细节,但了解该指令的功能可以真正洞悉执行Java程序的内部工作原理。 本文提供了一个新手的观点,即invokedynamic指令可以解决什么问题以及如何解决它。

方法句柄

方法句柄通常被描述为Java反射API的改进版本,但这并不是它们所代表的意思。 尽管方法句柄确实表示方法,构造函数或字段,但它们并不用于描述这些类成员的属性。 例如,不可能直接从方法句柄中提取元数据,例如所表示方法的修饰符或注释值。 尽管方法句柄允许引用方法的调用,但它们的主要目的是与invokedynamic调用站点一起使用。 为了更好地理解方法句柄,将它们视为反射API的不完美替代是一个合理的起点。

方法句柄无法实例化。 而是使用指定的查找对象创建方法句柄。 这些对象本身是使用MethodHandles类提供的工厂方法创建的。 每当调用工厂时,它都会首先创建一个安全上下文,以确保生成的查找对象只能定位对调用工厂方法的类也可见的方法。 然后可以按以下方式创建查找对象:

class Example {void doSomething() {MethodHandles.Lookup lookup = MethodHandles.lookup();}
}

如前所述,以上查找对象只能用于定位对Example类也可见的方法。 例如,不可能查找另一个类的私有方法。 这是使用反射API的第一个主要区别,反射API可以像定位其他任何方法一样定位外部类的私有方法,并且在将此类方法标记为可访问之后甚至可以调用这些方法。 因此,方法句柄对它们的创建上下文很敏感,这是与反射API的第一个主要区别。

除此之外,方法句柄通过描述特定类型的方法而不是仅代表任何方法,比反射API更具体。 在Java程序中,方法的类型是该方法的返回类型及其参数类型的组合。 例如,以下Counter类的only方法返回一个int,它表示唯一的String型参数的字符数:

class Counter {static int count(String name) {return name.length();}
}

可以使用另一个工厂来创建此方法类型的表示形式。 该工厂位于MethodType类中,该类还表示创建的方法类型的实例。 使用该工厂,可以通过移交方法的返回类型及其捆绑为数组的参数类型来创建Counter::count的方法类型:

MethodType methodType = MethodType.methodType(int.class, new Class<?>[] {String.class});

在描述上述方法的类型时,将方法声明为静态是很重要的。 编译Java方法时,非静态Java方法的表示方式类似于静态方法,但带有表示此伪变量的附加隐式参数。 因此,在为非静态方法创建MethodType时,需要传递代表该方法的声明类型的附加参数。 因此,对于上述Counter::count方法的非静态版本,方法类型将变为以下类型:

MethodType.methodType(int.class, Example.class, new Class<?>[] {String.class});

通过使用之前创建的查找对象以及上面的方法类型,现在可以找到代表Counter::count方法的方法句柄,如以下代码所示:

MethodType methodType = MethodType.methodType(int.class, new Class<?>[] {String.class});
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle methodHandle = lookup.findStatic(Counter.class, "count", methodType);
int count = methodHandle.invokeExact("foo");
assertThat(count, is(3));

乍一看,使用方法句柄似乎是使用反射API的过于复杂的版本。 但是,请记住,使用句柄直接调用方法并不是其使用的主要目的。

上面的示例代码和通过反射API调用方法的主要区别仅在研究Java编译器将两次调用转换为Java字节码的方式的区别时才显示出来。 当Java程序调用方法时,该方法由其名称,其(非通用)参数类型甚至其返回类型唯一标识。 因此,可以重载Java中的方法。 即使Java编程语言不允许这样做,但JVM从理论上还是允许按其返回类型重载方法。

遵循此原理,将反射方法调用作为Method :: invoke方法的公共方法调用执行。 此方法由其两个参数(类型为Object和Object [])标识。 除此之外,该方法还通过其对象返回类型来标识。 由于具有此签名,因此必须始终将该方法的所有参数装箱并包含在数组中。 同样,如果返回值是原始值,则需要将其装箱;如果该方法无效,则返回null。

方法句柄是该规则的例外。 不是通过引用MethodHandle::invokeExact签名来调用方法句柄,该签名将Object[]作为其单个参数并返回Object ,而是使用所谓的多态签名来调用方法句柄。 Java编译器根据调用现场的实际参数类型和期望的返回类型来创建多态签名。 例如,当使用

int count = methodHandle.invokeExact("foo");

Java编译器将转换此调用,就像将invokeExact方法定义为接受String类型的单个单个参数并返回int类型一样。 显然,这种方法不存在,并且对于(几乎)任何其他方法,这将在运行时导致链接错误。 对于方法句柄,Java虚拟机确实将此签名识别为多态的,并且将方法句柄的调用视为该句柄所引用的Counter::count方法是直接插入到调用站点中的。 因此,可以调用该方法而无需将原始值装箱或返回类型的开销,也无需将参数值放在数组内。

同时,在使用invokeExact调用时,向Java虚拟机保证方法句柄在运行时始终引用与多态签名兼容的方法。 对于该示例,JVM期望所引用的方法实际上接受String作为其唯一参数,并且它返回原始int 。 如果不满足此约束,则执行将导致运行时错误。 但是,任何其他接受单个String并返回原始int的方法都可以成功地填充到方法句柄的调用站点中,以替换Counter::count

相反,即使代码成功编译,在以下三个调用中使用Counter::count方法句柄也会导致运行时错误:

int count1 = methodHandle.invokeExact((Object) "foo");
int count2 = (Integer) methodHandle.invokeExact("foo");
methodHandle.invokeExact("foo");

第一条语句导致错误,因为传递给句柄的参数过于笼统。 虽然JVM期望将String作为方法的参数,但Java编译器建议该参数为Object类型。 重要的是要理解,Java编译器将强制转换作为创建不同的多态签名的提示,该签名将Object类型作为单个参数类型,而JVM在运行时期望使用String 。 请注意,此限制也适用于处理过于具体的参数,例如,将参数强制转换为Integer ,该方法的句柄需要使用Number类型作为其参数。 在第二条语句中,Java编译器建议运行时句柄的方法将返回Integer包装器类型,而不是原始int 。 而且,在第三条语句中根本没有建议返回类型,Java编译器将调用隐式转换为void方法调用。 因此, invokeExact确实意味着精确。

这种限制有时可能太苛刻。 出于这个原因,方法句柄不需要进行精确的调用,而在应用了诸如类型转换和拳击等转换的情况下,也允许进行更宽容的调用。 可以通过使用MethodHandle::invoke方法来应用MethodHandle::invoke 。 使用此方法,Java编译器仍会创建一个多态签名。 但是,这次Java虚拟机确实在运行时测试了实际参数和返回类型的兼容性,并在适当时通过应用装箱或转换来转换它们。 显然,这些转换有时会增加运行时的开销。

字段,方法和构造函数:作为统一接口处理

除了反射API的Method实例之外,方法句柄可以同样引用字段或构造函数。 因此,可以将MethodHandle类型的名称视为太窄。 实际上,在运行时通过方法句柄引用哪个类成员并不重要,只要它的MethodType (具有误导性的另一种类型)与在关联的调用站点传递的参数匹配即可。

使用MethodHandles.Lookup对象的适当工厂,可以查找一个字段以表示一个getter或setter。 在此上下文中使用getter或setter并不表示调用遵循Java Bean规范的实际方法。 而是,基于字段的方法句柄直接从字段读取或写入字段,但是通过调用方法句柄以方法调用的形式出现。 通过经由方法句柄表示此类字段访问,可以互换使用字段访问或方法调用。

以此类交换为例,采用以下类:

class Bean {String value;void print(String x) {System.out.println(x);}
}

给定此Bean类,可以使用以下方法句柄将字符串写到value字段或使用与参数相同的字符串调用print方法:

MethodHandle fieldHandle = lookup.findSetter(Bean.class, "value", String.class);
MethodType methodType = MethodType.methodType(void.class, new Class<?>[] {String.class});
MethodHandle methodHandle = lookup.findVirtual(Bean.class, "print", methodType);

只要在返回void同时将方法句柄调用站点与String一起传递给Bean的实例,则两个方法句柄可以互换使用,如下所示:

anyHandle.invokeExact((Bean) mybean, (String) myString);

与字段和方法类似,可以定位和调用构造函数。 此外,只要创建查找工厂的类可以访问此超级方法,则它不仅可以直接调用方法,甚至可以调用超级方法。 相反,在依赖反射API时根本不可能调用超级方法。 如果需要,甚至可以从句柄返回一个常数。

性能指标

方法句柄通常被描述为比Java反射API更高性能。 至少对于最新版本的HotSpot虚拟机而言,这不是事实。 证明这一点的最简单方法是编写适当的基准 。 再说一次,为Java程序编写基准并在执行时进行优化并不是一件容易的事。 编写基准的事实上的标准已成为使用JMH的工具,JMH是OpenJDK旗下的工具。 完整的基准可以在我的GitHub个人资料中找到要点。 本文仅涵盖此基准测试的最重要方面。

从基准来看,很明显反射已经非常有效地实现了。 现代JVM知道一个称为膨胀的概念,其中经常调用的反射方法调用被运行时生成的Java字节代码替换。 剩下的是将拳击用于传递参数和接收返回值的开销。 有时可以通过JVM的即时编译器消除这些拳击,但这并不总是可能的。 因此,如果方法调用涉及大量原始值,则使用方法句柄可能比使用反射API更有效。 但是,这确实要求在编译时已经知道确切的方法签名,以便可以创建适当的多态签名。 对于大多数反射API用例,由于在编译时不知道被调用方法的类型,因此无法提供此保证。 在这种情况下,使用方法句柄不会带来任何性能上的好处,因此不应替换它。

创建一个invokedynamic呼叫站点

通常,仅当Java编译器需要将lambda表达式转换为字节码时,才会创建invokedynamic调用站点。 值得一提的是,lambda表达式可以在没有完全调用动态调用站点的情况下实现,例如通过将它们转换为匿名内部类。 与建议的方法的主要区别是,使用invokedynamic会延迟创建与运行时类似的类。 我们将在下一部分中研究类的创建。 但是,现在请记住,invokedynamic与类创建无关,它仅允许将如何调度方法的决定延迟到运行时。

为了更好地理解invokedynamic调用站点,它有助于显式创建此类调用站点,以便单独查看机制。 为此,以下示例使用了我的代码生成框架Byte Buddy ,该框架提供了对invokedynamic调用站点的显式字节代码生成,而无需任何字节代码格式的知识。

任何invokedynamic调用站点最终都会产生一个MethodHandle,该MethodHandle引用要调用的方法。 但是,不是手动调用此方法句柄,而是由Java运行时决定。 因为方法句柄已成为Java虚拟机的已知概念,所以这些调用的优化类似于常见方法调用。 任何这样的方法句柄都是从所谓的引导程序方法接收的,而引导程序方法仅是满足特定签名的普通Java方法。 有关引导方法的简单示例,请查看以下代码:

class Bootstrapper {public static CallSite bootstrap(Object... args) throws Throwable {MethodType methodType = MethodType.methodType(int.class, new Class<?>[] {String.class})MethodHandles.Lookup lookup = MethodHandles.lookup();MethodHandle methodHandle = lookup.findStatic(Counter.class, "count", methodType);return new ConstantCallSite(methodHandle);}
}

目前,我们不太在乎该方法的参数。 相反,请注意,该方法是静态的,实际上是必需的。 在Java字节码中,invokedynamic调用站点引用引导程序方法的完整签名,但不引用可能具有状态和生命周期的特定对象。 一旦调用了invokedynamic调用站点,控制流便移交给引用的引导方法,该方法现在负责标识方法句柄。 从bootstrap方法返回此方法句柄后,它将由Java运行时调用。

从上面的示例可以明显看出, MethodHandle不是直接从引导方法返回的。 而是将句柄包装在CallSite对象的内部。 每当调用引导方法时,invokedynamic调用站点便会永久绑定到从此方法返回的CallSite对象。 因此,对于任何呼叫站点仅一次调用引导程序方法。 由于有了这个中间的CallSite对象,因此可以在以后交换引用的MethodHandle 。 为此,Java类库已经提供了CallSite不同实现。 在上面的示例代码中,我们已经看到了ConstantCallSite 。 顾名思义, ConstantCallSite始终引用相同的方法句柄,而不会在以后进行交换。 但是,也可以选择使用MutableCallSite ,它允许在以后的某个时间点更改引用的MethodHandle ,或者甚至有可能实现自定义的CallSite类。

通过上述引导程序方法和Byte Buddy,我们现在可以实现自定义invokedynamic指令。 为此,Byte Buddy提供了InvokeDynamic工具,该工具接受bootstrap方法作为其唯一的强制参数。 然后将这样的仪器馈送到Byte Buddy。 假设下面的类:

abstract class Example {abstract int method();
}

我们可以使用Byte Buddy来对Example进行子类化,以覆盖method 。 然后,我们将实现此方法以包含单个invokedynamic调用站点。 无需任何进一步的配置,Byte Buddy就会创建一个类似于覆盖方法的方法类型的多态签名。 请记住,对于非静态方法,此引用将作为第一个隐式参数处理。 假设我们要绑定将String作为单个参数的Counter::count方法,则无法将此句柄绑定到与方法类型不匹配的Example::method 。 因此,我们需要创建一个不带隐式参数但使用String代替的调用站点。 这可以通过使用Byte Buddy的域特定语言来实现:

Instrumentation invokeDynamic = InvokeDynamic.bootstrap(Bootstrapper.class.getDeclaredMethod(“bootstrap”, Object[].class)).withoutImplicitArguments().withValue("foo");

有了此工具,我们最终可以扩展Example类和override方法,以实现invokedynamic调用站点,如以下代码片段所示:

Example example = new ByteBuddy().subclass(Example.class).method(named(“method”)).intercept(invokeDynamic).make().load(Example.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION).getLoaded().newInstance();
int result = example.method();
assertThat(result, is(3));

从上面的断言可以明显看出, "foo"字符串的字符已正确计数。 通过在代码中设置适当的断点,还可以验证是否调用了bootstrap方法,并且控制流进一步到达了Counter::count方法。

到目前为止,使用invokedynamic调用站点并没有带来太多好处。 上面的bootstrap方法将始终绑定Counter::count ,因此只有在invokedynamic调用站点确实希望将String转换为int才能产生有效结果。 显然,由于引导方法从invokedynamic调用站点接收到的参数,因此引导方法可以更加灵活。 任何引导方法都至少接收三个参数:

作为第一个参数,bootstrap方法接收一个MethodHandles.Lookup对象。 该对象的安全性上下文是包含触发自举的invokedynamic调用站点的类的安全性上下文。 如前所述,这意味着可以使用此查找实例将定义类的私有方法绑定到invokedynamic调用站点。

第二个参数是一个表示方法名称的String 。 该字符串用作提示,指示从调用站点应将哪个方法绑定到它。 严格来说,不需要此参数,因为将方法与另一个名称绑定是完全合法的。 如果没有另外指定,Byte Buddy仅将覆盖方法的名称用作此参数。

最后,预期将返回的方法句柄的MethodType将用作第三个参数。 对于上面的示例,我们明确指定希望将String作为单个参数。 同时,Byte Buddy通过查看覆盖的方法得出我们需要int作为返回值,因为我们再次没有指定任何显式的返回类型。

引导程序方法的实现者应取决于引导程序方法的确切签名,只要它至少可以接受这三个参数即可。 如果引导程序方法的最后一个参数表示Object数组,则该最后一个参数将被视为varargs,因此可以接受任何多余的参数。 这也是上述示例引导程序方法有效的原因。

此外,引导程序方法可以从invokedynamic调用站点接收多个参数,只要这些参数可以存储在类的常量池中即可。 对于任何Java类,常量池都存储在类内部使用的值,主要是数字或字符串值。 到目前为止,此类常量可以是至少32位大小的原始值, StringClassMethodHandlMethodType 。 如果查找合适的方法句柄需要此类参数形式的其他信息,则可以使引导方法更灵活地使用。

Lambda表达式

每当Java编译器将lambda表达式转换为字节代码时,它都会将lambda的主体复制到定义该表达式的类内部的私有方法中。 这些方法被命名为lambda$X$Y其中X是包含lambda表达式的方法的名称,而Y是从零开始的序列号。 这种方法的参数是lambda表达式实现的功能接口的参数。 假定lambda表达式不使用非静态字段或封闭类的方法,则该方法也定义为静态的。

为了进行补偿,lambda表达式本身被invokedynamic调用站点替代。 在调用时,此调用站点请求为功能接口的实例绑定工厂。 作为该工厂的参数,调用站点提供了在表达式内部使用的lambda表达式的封闭方法的任何值,并在需要时提供对封闭实例的引用。 作为返回类型,要求工厂提供功能接口的实例。

为了引导呼叫站点,当前任何invokedynamic指令都委托给Java类库中包含的LambdaMetafactory类。 然后,该工厂负责创建一个实现功能接口的类,该类调用包含lambda主体的适当方法,该主体如前所述存储在原始类中。 但是,将来这种引导过程可能会改变,这是使用invokedynamic来实现lambda表达式的主要优点之一。 如果有一天,可以使用一种更适合的语言功能来实现lambda表达式,则可以将当前的实现替换掉。

为了能够创建实现功能接口的类,任何表示lambda表达式的调用站点都会为bootstrap方法提供其他参数。 对于强制性参数,它已经提供了功能接口方法的名称。 此外,它提供了引导应该产生的工厂方法的MethodType 。 此外,为引导方法提供了另一个MethodType ,它描述了功能接口方法的签名。 为此,它接收一个MethodHandle该方法引用包含lambda的方法主体的方法。 最后,调用站点提供了功能接口方法的通用签名的MethodType ,即在应用类型擦除之前在调用站点上方法的签名。

调用时,bootstrap方法将查看这些参数,并创建实现该功能接口的类的适当实现。 此类是使用ASM库创建的, ASM库是一种低级字节代码解析器和编写器,它已成为直接Java字节代码操作的事实上的标准。 bootstrap方法除了实现功能接口的方法外,还添加了适当的构造函数和静态工厂方法来创建类的实例。 此工厂方法后来绑定到invokedyanmic调用站点。 作为自变量,工厂将接收lambda方法的封闭实例的实例,以防其被访问以及从封闭方法中读取的任何值。

例如,考虑以下lambda表达式:

class Foo {int i;void bar(int j) {Consumer consumer = k -> System.out.println(i + j + k);}
}

为了执行,lambda表达式需要访问Foo的封闭实例及其封闭方法的值j。 因此,上述类的已废止版本看起来类似于以下内容,其中invokedynamic指令由某些伪代码表示:

class Foo {int i;void bar(int j) {Consumer consumer = <invokedynamic(this, j)>;}private /* non-static */ void lambda$foo$0(int j, int k) {System.out.println(this.i + j + k);}
}

为了能够调用lambda$foo$0 ,将封闭的Foo实例和j变量都传递到由invokedyanmic指令绑定的工厂。 然后,该工厂接收其所需的变量,以创建所生成类的实例。 然后,该生成的类将如下所示:

class Foo$$Lambda$0 implements Consumer {private final Foo _this;private final int j;private Foo$$Lambda$0(Foo _this, int j) {this._this = _this;this.j = j;}private static Consumer get$Lambda(Foo _this, int j) {return new Foo$$Lambda$0(_this, j);}public void accept(Object value) { // type erasure_this.lambda$foo$0(_this, j, (Integer) value);}
}

最终,生成的类的工厂方法通过ConstantCallSite包含的方法句柄绑定到invokedynamic调用站点。 但是,如果lambda表达式是完全无状态的,即不需要访问它所在的实例或方法,则LambdaMetafactory返回一个所谓的常量方法句柄,该句柄引用一个已创建的类的急切创建的实例。 因此,此实例用作单例,以便每次到达lambda表达式的调用站点时使用。 显然,此优化决策会影响应用程序的内存占用,并且在编写lambda表达式时要牢记这一点。 同样,没有工厂方法被添加到无状态lambda表达式的类中。

您可能已经注意到,lambda表达式的方法主体包含在一个私有方法中,该方法现在从另一个类调用。 通常,这将导致非法访问错误。 为了克服此限制,使用所谓的匿名类加载来加载生成的类。 仅当通过传递字节数组显式加载类时,才可以应用匿名类加载。 而且,通常无法在用户代码中应用匿名类加载,因为匿名类加载已隐藏在Java类库的内部类中。 当使用匿名类加载来加载类时,它会收到一个其继承其完整安全上下文的宿主类。 这涉及方法和字段访问权限以及保护域,因此也可以为签名的jar文件生成lambda表达式。 使用此方法,可以认为lambda表达式比匿名内部类更安全,因为私有方法永远无法从类外部访问。

隐藏:lambda表格

Lambda表单是虚拟机如何执行MethodHandles的实现细节。 由于其名称,lambda形式经常与lambda表达式混淆。 取而代之的是,lambda表单受lambda演算启发,并因此获得了名称,而不是因为它们在OpenJDK中实现lambda表达式的实际用法。

在OpenJDK 7的早期版本中,方法句柄可以两种方式之一执行。 方法句柄要么直接呈现为字节码,要么使用Java运行时提供的显式汇编代码进行分派。 字节码呈现已应用于在Java类的整个生命周期中被认为是完全恒定的任何方法句柄。 但是,如果JVM无法证明该属性,则通过将其分配给提供的汇编代码来执行方法句柄。 不幸的是,由于Java的JIT编译器无法优化汇编代码,因此导致了非恒定的方法句柄调用,从而“降低了性能”。 由于这也会影响延迟绑定的lambda表达式,因此,这显然不是令人满意的解决方案。

引入LambdaForm来解决此问题。 粗略地说,lambda形式表示字节码指令,如前所述,可以由JIT编译器对其进行优化。 在OpenJDK中,今天,方法句柄通过LambdaForm表示MethodHandle的调用语义。 通过这种可优化的中间表示,非恒定MethodHandle的使用变得更加MethodHandle 。 实际上,甚至有可能看到字节码编译的LambdaFormLambdaForm 。 只需将断点放在bootstrap方法内部或通过MethodHandle调用的方法内部。 断点LambdaForm可以在调用堆栈中找到字节码转换的LambdaForm

为什么这对动态语言很重要

应该在Java虚拟机上执行的任何语言都必须转换为Java字节码。 顾名思义,Java字节码与Java编程语言非常接近。 这包括为任何值定义严格类型的要求,并且在引入invokedynamic之前,需要一种方法调用来指定用于调度方法的显式目标类。 查看以下JavaScript代码,但是在将方法转换为字节码时无法指定任何信息:

function (foo) {foo.bar();
}

使用动态调用站点,可以将方法的调度程序的标识延迟到运行时,并且可以在需要更正先前决策的情况下重新绑定调用目标。 以前,使用具有所有性能缺陷的反射API是实现动态语言的唯一真正选择。

因此,invokedynamic指令的真正受益者是动态编程语言。 添加指令是使字节码格式与Java编程语言保持一致的第一步,这使JVM即使对于动态语言也成为强大的运行时。 而且,正如lambda表达式所证明的那样,这种将更多精力放在在JVM上托管动态语言的做法不会干扰Java语言的发展。 相反,Java编程语言是从这些努力中获得的。

翻译自: https://www.javacodegeeks.com/2015/04/dismantling-invokedynamic.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/360252.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CRON

http://blog.csdn.net/tianlesoftware/article/details/5315039 1. 同时修改文件的修改时间和访问时间 touch -d "2010-05-31 08:10:30" test.doc 2. 只修改文件的修改时间 touch -m -d "2010-05-31 08:10:30" test.doc 3. 只修改文件的访问时间 touch -a …

mysql innodb_file_format_Innodb表压缩过程中遇到的坑(innodb_file_format)

对于越来越多的数据&#xff0c;数据库的容量越来越大&#xff0c;压缩也就越来越常见了。在我的实际工作中进行过多次压缩工作&#xff0c;也遇到多次问题&#xff0c;在此和大家分享一下。首先&#xff0c;我们先说说怎么使用innodb的压缩.第一&#xff0c;mysql的版本需要大…

java内存泄漏和内存溢出_Java和内存泄漏

java内存泄漏和内存溢出总览 术语“内存泄漏”在Java中的使用方式不同于在其他语言中使用的方式。 通用术语中的“内存泄漏”是什么意思&#xff0c;在Java中如何使用&#xff1f; 维基百科的定义 当计算机程序消耗内存但无法将其释放回操作系统时&#xff0c;就会发生计算机科…

JSP内置对象之WEB安全性及config对象

一、WEB-INF的安全性是最高的。 在Java EE的标准中&#xff0c;Web目录中的WEB-INF是必须存在的&#xff0c;而且此文件夹的安全性是最高的&#xff0c;在各个程序的开发中&#xff0c;基本上都将一些配置信息保存在此文件夹中。在定义WEB-INF目录时一定要注意大小写的问题&…

一行中的Java 8 Lambda

如果您了解这一行&#xff0c;或者更好的是仍然可以编写此代码&#xff0c;则可以说您已经了解了Java 8 Lambda的本质。 当然&#xff0c;它们可以与集合一起使用。 我在Peter Lawrey最近的一次演讲中发现了这一点 。 &#xff08;有空余时间绝对值得观看整个演示文稿。&#…

mysql事务的重复性读_Mysql下InnoDB的可重复读级别的事务测试

Mysql下InnoDB的可重复读的事务测试### 背景&#xff1a;* mysql版本&#xff1a;Server version: 5.1.71* 操作系统&#xff1a;CentOS 6.5 X64* 事务隔离级别&#xff1a;不可重复读### 实验前的准备&#xff1a;mysql> use test;mysql> create table test_table(id in…

python动态绘图并保留之前绘图_[转]基于Python实现matplotlib中动态更新图片(交互式绘图)...

最近在研究动态障碍物避障算法&#xff0c;在Python语言进行算法仿真时需要实时显示障碍物和运动物的当前位置和轨迹&#xff0c;利用Anaconda的Python打包集合&#xff0c;在Spyder中使用Python3.5语言和matplotlib实现路径的动态显示和交互式绘图(和Matlab功能类似)。Anacond…

一步一步学Silverlight 2系列(25):综合实例之Live Search

概述 Silverlight 2 Beta 1版本发布了&#xff0c;无论从Runtime还是Tools都给我们带来了很多的惊喜&#xff0c;如支持框架语言Visual Basic, Visual C#, IronRuby, Ironpython&#xff0c;对JSON、Web Service、WCF以及Sockets的支持等一系列新的特性。《一步一步学Silverlig…

gateway中的局部过滤器_Spring Cloud Gateway中的过滤器工厂:重试过滤器

Spring Cloud Gateway基于Spring Boot 2&#xff0c;是Spring Cloud的全新项目&#xff0c;该项目提供了一个构建在Spring 生态之上的API网关。本文基于的Spring Cloud版本为Finchley M9&#xff0c;Spring Cloud Gateway对应的版本为2.0.0.RC1。Spring Cloud Gateway入门一文介…

MySql命令行基本操作

启动mysql服务&#xff1a; net start mysql关闭mysql服务&#xff1a; net stop mysql命令行登陆mysql&#xff1a;mysql -h localhost -u root -p admin 命令行登陆mysql&#xff1a;mysql -uroot -padmin 退出mysql控制台&#xff1a;quit或者exit查看mysql控制台当前信息&a…

mysql 5.7 启动脚本_MySQL数据库 5.7 启动脚本

本文主要向大家介绍了MySQL数据库 5.7 启动脚本&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习MySQL数据库有所帮助。最近这段时间&#xff0c;在看mysql&#xff0c;安装了&#xff0c;也应用过&#xff0c;对于生产环境中&#xff0c;一般都选择使用source…

cocos2d-x for android配置 运行 Sample on Linux OS

1.从http://www.cocos2d-x.org/download下载稳定版 比如cocos2d-x-2.2 2.解压cocos2d-x-2.2.zip,比如本文将其解压到 /opt 目录下 3.运行 android-buildsetup.sh,运行之前需要先设置3个环境变量,如将以下变量写到文件 /etc/profile中 export ANDROID_SDK_ROOT/opt/android-sdk-…

转变馆藏

您是否曾经想替换过HashSet或HashMap使用的equals和hashCode方法&#xff1f; 或者有一个List的一些元素类型伪装成的List相关类型的&#xff1f; 转换集合使这成为可能&#xff0c;并且本文将展示如何实现。 总览 转换集合是LibFX 0.3.0的一项功能&#xff0c;该功能将在今天…

mysql 保证事物完整性_数据库高并发请求,如何保证数据完整性?详解MySQL/InnoDB的加锁...

本文是对MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解&#xff0c;这些在面试中也经常遇到&#xff0c;如数据库高并发请求&#xff0c;如何保证数据完整性&#xff1f;今天我查阅资料进行了MySQL/InnoDB中加锁知识点的汇总&#xff0c;这样也会…

Dll学习一_Dll 创建并动态引用窗体且释放窗体Demo

1、新建Dll工程 2、Dll工程全部代码 library SubMain;{ Important note about DLL memory management: ShareMem must be thefirst unit in your librarys USES clause AND your projects (selectProject-View Source) USES clause if your DLL exports any procedures orfunct…

Java擦除

概述&#xff1a; Java泛型在使用过程有诸多的问题&#xff0c;如不存在List<String>.class, List<Integer>不能赋值给List<Number>&#xff08;不可协变&#xff09;&#xff0c;奇怪的ClassCastException等。 正确的使用Java泛型需要深入的了解Java的一些概…

mysql数据库相互备份_MySQL的本地备份和双机相互备份脚本

先修改脚本进行必要的配置,然后以root用户执行.1. 第一执行远程备份时先用 first参数.2. 本地备份用local参数3. 远程备份不用参数注意:需要在另一主机上的Mysql用户用添加用户..需要配置的地方:# define host and mysql passwordREMOTE_HOST"" #远程主机名或IPREMOT…

Spring Boot和Swagger UI

我已经一年没有从头开始开发Spring Web应用程序了&#xff0c;如果我不参加QA自动化工程师的培训&#xff0c;那么这段时间甚至会更长。 由于这个原因&#xff0c;我开发了一个示例REST应用程序。 除了Swagger&#xff0c;一切对我来说都很熟悉。 因此&#xff0c;我将描述我在…

mysql5.7.22打不开_windows下mysql-5.7.22-winx64突然启动不了,报错Could not open log file

本文摘自classinstance.cn。windows下mysql-5.7.22-winx64突然启动不了&#xff0c;感觉启动几秒钟后就自己关闭了&#xff0c;看了下启动日志&#xff1a;2019-08-25T10:57:08.389404Z 0 [Warning] option wait_timeout: unsigned value 31536000 adjusted to 21474832019-08-…

HDU1530 最大流问题

第一次写Dinic 然后贴一下 最基础的网络流问题 嘎嘎: #include <iostream> #include<cstdio> #include<string.h> #include<queue> using namespace std; const int M205; __int64 map[M][M]; int n,m,dist[M]; queue<int>q; void readdate() {_…