7

I'm running the following Java program on a Macbook Air running OSX 10.9.4, 1.7GHz i7, 8GB memory. I have the Java Cryptography Extension (JCE) installed.

import javax.crypto.Mac;

public class Main {
  public static void main(String[] args) throws Exception {
    Mac.getInstance("HmacSHA1");
  }
}

Running this simple program results in a run time of over 5 seconds!

$ javac -version
javac 1.7.0_45
$ javac Main.java
$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
$ time java Main

real    0m5.326s
user    0m0.390s
sys     0m0.033s

I've done a bunch of searching but haven't found much in the way of workarounds or explanation.

Has anyone heard of this problem before? Or know of a way for me to debug what's going on? It's really frustrating to take a 5 second penalty to your unit tests when they've been instantaneous up until now.

Edit: Here's timing inside the program and the list of security providers:

import java.security.Provider;
import java.security.Security;
import javax.crypto.Mac;

public class Main {
  public static void main(String[] args) throws Exception {
    for (Provider p: Security.getProviders()) {
        System.out.println(p.getName() + " " + p.getVersion() + " " + p.getInfo());
    }
    long start = System.currentTimeMillis();
    Mac.getInstance("HmacSHA1");
    System.out.println(System.currentTimeMillis() - start + "ms");
  }
}

$ java Main
SUN 1.7 SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS keystore; PKIX CertPathValidator; PKIX CertPathBuilder; LDAP, Collection CertStores, JavaPolicy Policy; JavaLoginConfig Configuration)
SunRsaSign 1.7 Sun RSA signature provider
SunEC 1.7 Sun Elliptic Curve provider (EC, ECDSA, ECDH)
SunJSSE 1.7 Sun JSSE provider(PKCS12, SunX509 key/trust factories, SSLv3, TLSv1)
SunJCE 1.7 SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)
SunJGSS 1.7 Sun (Kerberos v5, SPNEGO)
SunSASL 1.7 Sun SASL provider(implements client mechanisms for: DIGEST-MD5, GSSAPI, EXTERNAL, PLAIN, CRAM-MD5, NTLM; server mechanisms for: DIGEST-MD5, GSSAPI, CRAM-MD5, NTLM)
XMLDSig 1.0 XMLDSig (DOM XMLSignatureFactory; DOM KeyInfoFactory)
SunPCSC 1.7 Sun PC/SC provider
Apple 1.1 Apple Provider
5224ms

Edit 2:

Figured out how to run HPROF on the code.

$ java -agentlib:hprof=cpu=times Main
$ cat java.hprof.txt
...
TRACE 308670:
java.net.InetAddress$1.lookupAllHostAddr(InetAddress.java:Unknown line)
java.net.InetAddress.getAddressesFromNameService(InetAddress.java:Unknown line)
java.net.InetAddress.getLocalHost(InetAddress.java:Unknown line)
javax.crypto.JarVerifier.getSystemEntropy(JarVerifier.java:Unknown line)
...
CPU TIME (ms) BEGIN (total = 6680) Sat Aug 16 05:59:39 2014
rank   self  accum   count trace method
   1 74.87% 74.87%       1 308670 java.net.InetAddress$1.lookupAllHostAddr
...

So, it appears that for some reason the JarVerifier is trying to get entropy from the system and this is causing the program to spend 5 seconds in InetAddress$1.lookupAllHostAddr...

Edit 3:

Updating Java to "1.7.0_67" does not fix the problem.

Community
  • 1
  • 1
Axle
  • 371
  • 2
  • 8
  • 1
    I'm running with OSX 10.9.3 on a 2009 iMac and it takes 0.148 seconds to run this code. To ensure you're measuring the right thing, add some time measurements using `System.currentTimeMillis()` *inside* your Java program, so you're not measuring your JVM startup time. Because .. your problem could be a faulty or heavily fragmented disk that causes your JVM to start up slowly. – Erwin Bolwidt Aug 15 '14 at 04:41
  • Edited to add timing inside the program. – Axle Aug 15 '14 at 05:14
  • 1
    Could you list the providers you have installed when running that program (`Security.getProviders()`), they are in order of priority. – Maarten Bodewes Aug 15 '14 at 13:30
  • Edited to add printing of the list of security providers. Thank you for asking, you reminded me that I do have the Java Crypto Extension installed. – Axle Aug 15 '14 at 14:01
  • 1
    Vexing. No specific provider that should take a long time. No other libs with large static initializers (e.g. Bouncy Castle in the classpath)? The code exits almost immediately on my Linux system as well. Compare with a fresh install otherwise, just to see if there are any side effects. – Maarten Bodewes Aug 16 '14 at 08:21
  • 1
    Is there a ten-second delay if you duplicate the `Mac.getInstance("HmacSHA1");` line, or does the delay only occur the first time you invoke that method? – ntoskrnl Aug 16 '14 at 13:04
  • No significant additional time is added when duplicating the getInstance call. However, owlstead inspired me to figure out a profiler and I've added an edit above with the apparent culprit. – Axle Aug 16 '14 at 13:17

1 Answers1

9

I've found a solution. As can be seen in Edit 2, Mac.getInstance() appears to call javax.crypto.JarVerifier.getSystemEntropy(), which eventually calls java.net.InetAddress.getLocalHost(). According to this article, Java 7 changed the way InetAddress looks for local host. For some reason, this causes my machine to lookupAllHostAddr, which takes ~5 seconds to finish.

One of the comments on the article lists the solution which worked for me, which was to add my hostname to /etc/hosts. The following:

127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost

changes to

127.0.0.1       localhost     <replace-me>.local
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost

After the change to the hosts file, my time is back to a reasonable 200ms.

$ time java Main

real    0m0.242s
user    0m0.379s
sys 0m0.034s
Axle
  • 371
  • 2
  • 8
  • 2
    +1, you did an excellent job tracking this down. I was also looking at the bytecode and getSystemEntropy() does other things that can slow you down, like if you have a million files in your temp directory (property "java.io.tmpdir"). – President James K. Polk Aug 16 '14 at 21:43
  • The java.io.tmpdir issue mentioned by @GregS is one I've bumped into many times. Need to keep files from building up here or you will face this issue. – greghmerrill May 04 '15 at 18:47
  • is the value of should be command `hostname` output? – treesong May 24 '18 at 02:57