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.
Mac.getInstance() for HmacSHA1 taking ages to execute
This sounds similar to my problem, but all sources I've read indicate that /dev/random
and /dev/urandom
are the same on OSX.
How to solve performance problem with Java SecureRandom?
Again, discussions about SecureRandom's source of randomness, but doesn't seem to apply to OSX.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With