2016-08-14 7 views
44

mam następujący program Java:prosty program Java 100 razy wolniej po podłączeniu do Internetu USB

class Main { 
    public static void main(String[] args) throws java.io.IOException { 
     long start = System.nanoTime(); 
     java.io.File.createTempFile("java_test", ".txt").delete(); 
     System.out.println((System.nanoTime() - start)/1e9); 
    } 
} 

Normalnie, trwa walka 63 milisekund do wykonania:

$ java Main 
0.06308555 

Ale po podłączeniu Android telefon jako hotspot USB, trwa znacznie dłużej. W zależności od urządzenia w dowolnym miejscu od 3 do 40 sekund:

$ java Main 
4.263285528 

Najdziwniejsze jest to, że nic nie jest tu faktycznie przesyłane w sieci - włączony do gniazda sieciowego karty sieciowe nie powinno mieć znaczenia.

zrobiłem ślad i wygląda na to, większość czasu spędza w NetworkInterface.getAll metody:

"main" #1 prio=5 os_prio=0 tid=0x00000000023ae000 nid=0x142c runnable [0x000000000268d000] 
    java.lang.Thread.State: RUNNABLE 
     at java.net.NetworkInterface.getAll(Native Method) 
     at java.net.NetworkInterface.getNetworkInterfaces(Unknown Source) 
     at sun.security.provider.SeedGenerator.addNetworkAdapterInfo(Unknown Source) 
     at sun.security.provider.SeedGenerator.access$000(Unknown Source) 
     at sun.security.provider.SeedGenerator$1.run(Unknown Source) 
     at sun.security.provider.SeedGenerator$1.run(Unknown Source) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at sun.security.provider.SeedGenerator.getSystemEntropy(Unknown Source) 
     at sun.security.provider.SecureRandom$SeederHolder.<clinit>(Unknown Source) 
     at sun.security.provider.SecureRandom.engineNextBytes(Unknown Source) 
     - locked <0x000000076afa2820> (a sun.security.provider.SecureRandom) 
     at java.security.SecureRandom.nextBytes(Unknown Source) 
     - locked <0x000000076af6bdc8> (a java.security.SecureRandom) 
     at java.security.SecureRandom.next(Unknown Source) 
     at java.util.Random.nextLong(Unknown Source) 
     at java.io.File$TempDirectory.generateFile(Unknown Source) 
     at java.io.File.createTempFile(Unknown Source) 
     at java.io.File.createTempFile(Unknown Source) 
     at Main.main(Main.java:4) 

który z kolei wydaje się spędzać większość czasu w GetIfTable systemu Windows metody API:

Child-SP   RetAddr   Call Site 
00000000`0257ed78 000007fe`fd7210ba ntdll!NtDeviceIoControlFile+0xa 
00000000`0257ed80 000007fe`fd721252 nsi+0x10ba 
00000000`0257ee20 000007fe`fd7211f9 nsi!NsiEnumerateObjectsAllParametersEx+0x2e 
00000000`0257ee60 000007fe`fd7217b0 nsi!NsiEnumerateObjectsAllParameters+0xc9 
00000000`0257ef00 000007fe`f9c7928d nsi!NsiAllocateAndGetTable+0x184 
00000000`0257efd0 00000000`6f8c5a01 IPHLPAPI!GetIfTable+0xa9 
00000000`0257f090 00000000`6f8c6980 net!Java_java_net_NetworkInterface_getMTU0+0x1a1 
00000000`0257f150 00000000`6f8c6e57 net!Java_java_net_NetworkInterface_isP2P0_XP+0x88 
00000000`0257f270 00000000`6f8c6058 net!Java_java_net_NetworkInterface_getAll_XP+0x23 
00000000`0257f2a0 00000000`02867f54 net!Java_java_net_NetworkInterface_getAll+0x2c 

GetIfTable wydaje się być problematyczną funkcją. Ja obserwując samą spowolnienie zarówno w przykładzie programu z: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365943(v=vs.85).aspx i z następującym fragmencie:

#include <iphlpapi.h> 
#include <stdlib.h> 

int main() { 
    DWORD dwSize = sizeof(MIB_IFTABLE); 
    MIB_IFTABLE *pIfTable = malloc(dwSize); 
    GetIfTable(pIfTable, &dwSize, FALSE); 
    pIfTable = malloc(dwSize); 
    GetIfTable(pIfTable, &dwSize, FALSE); 
    return 0; 
} 

jak to naprawić lub obejście tego problemu? Mogę samodzielnie tworzyć pliki tymczasowe i unikać wywoływania interfejsu NetworkInterface.getNetworkInterfaces, ale SecureRandom jest używany w całej standardowej bibliotece Java. Czy istnieje sposób zmuszenia SecureRandom, aby nie korzystał z GetIfTable?

wersja Java:

> java -version 
java version "1.8.0_101" 
Java(TM) SE Runtime Environment (build 1.8.0_101-b13) 
Java HotSpot(TM) 64-Bit Server VM (build 25.101-b13, mixed mode) 

wersja Windows:

OS Name:     Microsoft Windows 7 Professional 
OS Version:    6.1.7601 Service Pack 1 Build 7601 

problematyczna karta sieciowa:

Name [00000020] Remote NDIS based Internet Sharing Device 
Adapter Type Ethernet 802.3 
Product Type Remote NDIS based Internet Sharing Device 
Installed Yes 
PNP Device ID USB\VID_0FCE&PID_71C4&MI_00\7&6BE3F3B&0&0000 
Last Reset 8/14/2016 12:26 PM 
Index 20 
Service Name usb_rndisx 
IP Address 192.168.42.183, fe80::90ab:3786:4396:2870 
IP Subnet 255.255.255.0, 64 
Default IP Gateway 192.168.42.129 
DHCP Enabled Yes 
DHCP Server 192.168.42.129 
DHCP Lease Expires 8/14/2016 3:27 PM 
DHCP Lease Obtained 8/14/2016 2:27 PM 
MAC Address 02:18:61:77:7D:72 
Driver c:\windows\system32\drivers\usb8023x.sys (6.1.7600.16385, 19.50 KB (19,968 bytes), 7/14/2009 2:09 AM) 
+0

Może to nie jest problem z Javą. Jeśli podłączę mój smartfon jako hotspot USB, prawie każdy wolumin jest wolniejszy. Zwykle trwa to około 5 minut, aby otworzyć np. Ustawienia sieciowe w oknach. – andii1997

+0

Na marginesie: ta powolna inicjalizacja dzieje się zawsze w życiu JVM, kiedy jakiś kod używa po raz pierwszy 'java.util.Random'. Dlatego kolejne wywołania 'java.io.File.createTempFile' nie powinny być tak wolne. Być może nie jest to dla ciebie duży problem, w zależności od twojego przypadku użycia. –

+8

Kolejna uwaga: zdumiewające, jak głęboko zbadałeś ten problem, zanim zapytasz tutaj. Nauczyłem się z twojego pytania. Dzięki. –

Odpowiedz

29

Domyślna implementacja SecureRandomscans network interfaces jako dodatkowe źródło entropii układu. Aby tego uniknąć, musisz zarejestrować niestandardową wersję java.security.Provider, która zawiera inną implementację SecureRandomSpi.

Na szczęście JDK dla Windows ma już odpowiednią implementację SecureRandomSpi opartą na Microsoft Crypto API: sun.security.mscapi.PRNG. Chociaż jest to niepubliczny interfejs API, klasa istnieje we wszystkich wersjach OpenJDK i Oracle JDK od 1.6 do 9, a mimo to awarie są dostępne.

Istnieją dwa sposoby zarejestrowania programu MS Crypto PRNG jako domyślnego algorytmu SecureRandom.

1. Z poziomu aplikacji pod numerem WindowsSecureRandom.register() na samym początku.

import java.security.Provider; 
import java.security.Security; 

public class WindowsSecureRandom extends Provider { 
    private static final String MSCAPI = "sun.security.mscapi.PRNG"; 

    private WindowsSecureRandom() { 
     super("WindowsSecureRandom Provider", 1.0, null); 
     putService(new Service(this, "SecureRandom", "Windows-PRNG", MSCAPI, null, null)); 
    } 

    public static void register() { 
     if (System.getProperty("os.name").contains("Windows")) { 
      try { 
       Class.forName(MSCAPI); 
       Security.insertProviderAt(new WindowsSecureRandom(), 1); 
      } catch (ClassNotFoundException e) { 
       // Fallback to default implementation 
      } 
     } 
    } 
} 

2. poprzez uporządkowanie listy dostawców w %JAVA_HOME%\jre\lib\security\java.security pliku.

security.provider.1=sun.security.mscapi.SunMSCAPI <<<--- make it the first provider 
security.provider.2=sun.security.provider.Sun 
security.provider.3=sun.security.rsa.SunRsaSign 
security.provider.4=sun.security.ec.SunEC 
security.provider.5=com.sun.net.ssl.internal.ssl.Provider 
... 

I zostały zweryfikowane, że albo z rozwiązaniami SeedGenerator i NetworkInterface zajęcia nie są już załadowane.

+0

Wygląda na to, że to zadziała. Trochę się martwię o nadużywanie takich nie-API. –

+0

@Banthar Znalazłem inne, bardziej przejrzyste rozwiązanie, które nie wymaga łatania klas systemowych. – apangin

Powiązane problemy