“Discipline” is a difficult word for most of us. It conjures up images of somebody standing over you with a stick, telling you that you’re wrong. But self-discipline is different. It’s the skill of seeing through the hollow shouting of your own impulses and piercing their secret. They have no power over you. It’s all a show, a deception. Your urges scream and bluster at you; they cajole; they coax; they threaten; but they really carry no stick at all. You give in out of habit. You give in because you never really bother to look beyond the threat. It is all empty back there. There is only one way to learn this lesson, though. The words on this page won’t do it. But look within and watch the stuff coming up—restlessness, anxiety, impatience, pain—just watch it come up and don’t get involved. Much to your surprise, it will simply go away. It rises, it passes away. As simple as that. There is another word for self-discipline. It is patience.
-- Bhante Henepola Gunaratana
If you're reading this, then the migration to Vultr was successful. Additionally, this site should now be accessible to IPv6 users.
Host | IPv4 | IPv6 ------------------------------------------------------------------------ io7m.com | 45.77.78.222 | 2001:19f0:0005:061d:f000:0000:0000:0000/64 mail.io7m.com | 45.77.76.92 | 2001:19f0:0005:0752:f000:0000:0000:0000/64
I'm looking at changing my VPS provider from
DigitalOcean to
Vultr. These were the top two contenders
when I initially chose a provider. I can't fault DigitalOcean's
service, but Vultr have better pricing and give more control over
DNS details such as
PTR records.
I have the configurations ready to go, so I suspect I'll make the
move over the next few days. I'll be taking this opportunity to
enable IPv6 for the http
and smtp services. Expect outages!
A little known feature of javac is that it will inline constant
references when compiling code. This can mean that it's possible to
accidentally break binary compatibility with existing clients of a
piece of code when changing the value of a constant. Worse, tools
that analyze bytecode have no way of detecting a binary-incompatible
change of this type.
For example, the following class defines a public constant called NAME:
public final class Constants
{
public static final String NAME = "com.io7m.name";
private Constants()
{
}
}
Another class refers to NAME directly:
public final class Main0
{
public static void main(
final String args[])
{
System.out.println(Constants.NAME);
}
}
Now, let's assume that NAME actually becomes part of an API in
some form; callers may pass NAME to API methods. Because we've
taken the time to declare a global constant, it should be perfectly
safe to change the value of NAME at a later date without having
to recompile all clients of the API, yes? Well, no, unfortunately
not. Take a look at the bytecode of Main0:
public final class Main0
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Methodref #7.#16 // java/lang/Object."<init>":()V
#2 = Fieldref #17.#18 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Class #19 // Constants
#4 = String #20 // com.io7m.name
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#5 = Methodref #21.#22 // java/io/PrintStream.println:(Ljava/lang/String;)V
#6 = Class #23 // Main0
#7 = Class #24 // java/lang/Object
...
#19 = Utf8 Constants
#20 = Utf8 com.io7m.name
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#21 = Class #28 // java/io/PrintStream
#22 = NameAndType #29:#30 // println:(Ljava/lang/String;)V
...
{
public Main0();
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 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #4 // String com.io7m.name
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
}
You can see that the value of the NAME constant has been inlined
and inserted into the Main0 class's constant pool directly. This
means that if you change the value of NAME in the Constants
class at a later date, the Main0 class will need to be recompiled
in order to see the change.
What can be done instead? Wrap the constant in a static method:
public final class ConstantsWrapped
{
private static final String NAME = "com.io7m.name";
public static final String name()
{
return NAME;
}
private ConstantsWrapped()
{
}
}
Call the method instead of referring to the constant directly:
public final class Main1
{
public static void main(
final String args[])
{
System.out.println(ConstantsWrapped.name());
}
}
Now the resulting bytecode is:
public final class Main1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #18.#19 // ConstantsWrapped.name:()Ljava/lang/String;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#4 = Methodref #20.#21 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #22 // Main1
#6 = Class #23 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Main1.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Class #24 // java/lang/System
#17 = NameAndType #25:#26 // out:Ljava/io/PrintStream;
#18 = Class #27 // ConstantsWrapped
#19 = NameAndType #28:#29 // name:()Ljava/lang/String;
#20 = Class #30 // java/io/PrintStream
#21 = NameAndType #31:#32 // println:(Ljava/lang/String;)V
#22 = Utf8 Main1
#23 = Utf8 java/lang/Object
#24 = Utf8 java/lang/System
#25 = Utf8 out
#26 = Utf8 Ljava/io/PrintStream;
#27 = Utf8 ConstantsWrapped
#28 = Utf8 name
#29 = Utf8 ()Ljava/lang/String;
#30 = Utf8 java/io/PrintStream
#31 = Utf8 println
#32 = Utf8 (Ljava/lang/String;)V
{
public Main1();
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 1: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #3 // Method ConstantsWrapped.name:()Ljava/lang/String;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
9: return
LineNumberTable:
line 6: 0
line 7: 9
}
This effectively solves the issue. The ldc opcode is changed to an
invokestatic opcode, at no point does the string com.io7m.name
appear directly in the Main1 class, and the value of the constant
can be changed at a later date without breaking binary compatibility.
Additionally, the JIT compiler will inline the invokestatic call
at run-time, meaning that there's no performance degradation over
using the constant directly.