-D socksProxyHost and -DsocksProxyPort. I wanted to see if I could find a way to work around this limitation, possibly by doing evil things with reflection.The problems boils down to the fact that with NIO, you use a
SocketChannel, and under the hood, some kind of a Socket must be used. Here the fun starts, because as the javadoc says, "The actual work of the socket is performed by an instance of the SocketImpl class", which is itself an abstract class (it's an abstract Impl, yeah, right...).So let's see how the code goes from a
SocketChannel to a Socket or a SocketImpl:- People typically create a
SocketChannellike so:SocketChannel.open() SocketChannel.open()doesreturn SelectorProvider.provider().openSocketChannel();SelectorProvider.provider()checks to see if the system propertyjava.nio.channels.spi.SelectorProvideris set and a few other things. Unless you specifically do something manually, none of that stuff will end up finding a provider, so the code will end up doingsun.nio.ch.DefaultSelectorProvider.create();and returning that.- Now here things get a little bit blurry. I think we end up in here (on Linux) doing
return new sun.nio.ch.EPollSelectorProvider(); - So in step 2., when
openSocketChannel()is called on the provider, we end up in here, doingreturn new SocketChannelImpl(this); SocketChannelImplthen does all the work for NIO sockets, mostly usingsun.nio.ch.Netwhich is chiefly made of native methods.- If you want to view a
SocketChannelImplas aSocketby callingsocket(), theSocketChannelImplwraps itself in aSocketAdaptor, but the adaptor simply transforms all the calls to calls on the underlyingSocketChannelImplwhich uses the native functions insun.nio.ch.Net, so there's nothing really you can do to make it use a SOCKS proxy.
Also, just for the fun of it, I was curious to see how many objects were involved under the hood of a single
SocketChannel. The NIO code is so bloated that I was expecting quite a bit of overhead. I used the following not-very-scientific method (which is kinda similar to PMP) to try to get an answer:$ cat t.javathen I run this code and use
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
final class t {
public static void main(String[]a) throws Exception {
System.out.println("ready");
Thread.sleep(5000);
SocketChannel socket = SocketChannel.open();
InetSocketAddress addr = new InetSocketAddress("www.google.com", 80);
socket.connect(addr);
System.out.println("connected");
Thread.sleep(5000);
}
}
jmap -histo:live right after seeing the "ready" message and right after seeing the "connected" message. In between both of those messages, 2933 objects occupying 371744 bytes of RAM (on my MBP with the JDK 1.6.0_20-b02-279-10M3065 and HotSpot JVM 16.3-b01-279, both shipped by Apple with OS X 10.6.4). I don't know if I'm supposed to laugh or cry.<methodKlass> +615 +74264Er.. OK I guess that wasn't entirely fair, since 2286 objects (taking 328928 bytes of RAM) have been created by the VM for classes that have been loaded and compiled. So if we discount those, we're still left with 647 objects taking 42816 bytes of RAM... Ahem... Just for a little
<constMethodKlass> +615 +72416
<constantPoolKlass> +65 +47288
<instanceKlassKlass> +65 +46264
<symbolKlass> +705 +33312
<constantPoolCacheKlass> +67 +32024
java.lang.Class +69 +12696
[C +85 +10664
[[I +121 +8760
[I +73 +5928
[S +96 +5232
[B +58 +4600
java.lang.String +94 +3760
java.util.LinkedList$Entry +65 +2600
<objArrayKlassKlass> +4 +2336
<methodDataKlass> +4 +1800
java.net.URL +15 +1560
java.util.HashMap$Entry +29 +1392
[Ljava.util.HashMap$Entry; +8 +1344
java.util.LinkedHashMap$Entry +15 +960
java.util.LinkedList +23 +920
sun.misc.URLClassPath$JarLoader +6 +432
java.util.HashMap +6 +384
java.io.ExpiringCache$Entry +11 +352
java.util.jar.JarFile$JarFileEntry +3 +336
java.lang.reflect.Constructor +2 +240
[Ljava.lang.Object; +2 +208
java.net.Inet4Address +5 +200
sun.nio.ch.SocketChannelImpl +1 +176
java.util.LinkedHashMap +2 +160
java.lang.Object +8 +128
java.lang.ThreadLocal +5 +120
java.lang.ClassLoader$NativeLibrary +2 +96
[Ljava.net.InetAddress; +2 +88
java.util.ArrayList +2 +80
sun.misc.JarIndex +2 +80
[Ljava.lang.String; +2 +64
java.net.InetAddress$CacheEntry +2 +64
java.net.InetAddress$Cache +2 +64
java.net.InetAddress$Cache$Type +2 +64
sun.misc.URLClassPath +1 +56
java.lang.ref.SoftReference +1 +56
[Ljava.lang.ThreadLocal; +1 +48
java.util.Stack +1 +40
sun.reflect.NativeConstructorAccessorImpl +1 +40
java.net.InetSocketAddress +1 +40
[Ljava.net.InetAddress$Cache$Type; +1 +40
[Ljava.lang.reflect.Constructor; +1 +32
java.net.Inet6AddressImpl +1 +32
sun.reflect.DelegatingConstructorAccessorImpl +1 +24
java.util.HashMap$KeySet +1 +24
java.nio.channels.spi.AbstractInterruptibleChannel$1 +1 +24
[Ljava.lang.Class; +1 +24
java.net.InetAddress$1 +1 +16
sun.net.www.protocol.jar.Handler +1 +16
sun.nio.ch.KQueueSelectorProvider +1 +16
sun.nio.ch.SocketDispatcher +1 +16
java.io.FileDescriptor -1 -24
java.util.Vector -1 -40
java.io.FileInputStream -2 -64
java.util.jar.JarFile -1 -80
java.util.zip.ZStreamRef -4 -96
java.util.zip.Inflater -4 -192
java.util.zip.ZipFile$ZipFileInputStream -12 -672
java.lang.ref.Finalizer -17 -1088
SocketChannel.PS: I'm half-ashamed but to produce the output above, I used what could possibly be the most horrible "one-liner" I ever wrote in Python. I put the output of
jmap in /tmp/a for the first run and /tmp/b for the second run:python -c 'load = lambda path: dict((klass, (int(instances), int(bytes))) for (num, instances, bytes, klass) in (line.split() for line in open(path) if line[4] == ":")); a = load("/tmp/a"); b = load("/tmp/b"); print "\n".join("%s\033[55G%+4d\t%+6d" % (klass, diffinst, diffbytes) for (klass, diffinst, diffbytes) in sorted(((klass, b[klass][0] - a.get(klass, (0, 0))[0], b[klass][1] - a.get(klass, (0, 0))[1]) for klass in b), key=lambda i: i[2], reverse=True) if diffbytes != 0).replace(">", "<").replace(">", ">")'
0 comments:
Post a Comment