NIO DatagramChannel接收发送数据

前言:

首先心血来潮,因为代码用到ByteBuffer buffer=ByteBuffer.allocate(2048);

我就突然想到,到底allocate和allocateDirect区别精髓在哪里,结果如下:

在NIO中,使用ByteBuffer分配缓存区的方式有哪些?



一、创建Buffer对象的方式?

1、从JVM堆中分配内存,

2、也可以OS本地内存中分配,由于本地缓冲区避免了缓冲区复制,在性能上相对堆缓冲区有一定优势,但同时也存在一些弊端。

二、两种缓冲区对应的API如下:

1、JVM堆缓冲区:ByteBuffer.allocate(size)

2、本地缓冲区:ByteBuffer.allocateDirect(size)//属于系统级的内存开销,就是操作系统直接分配的

三、ByteBuffer对象的垃圾回收策略?

1、从堆中分配的缓冲区为普通的Java对象,生命周期与普通的Java对象一样,当不再被引用时,Buffer对象会被回收。

2、直接缓冲区(DirectBuffer)为本地内存,并不在Java堆中,也不能被JVM垃圾回收。由于直接缓冲区在JVM里被包装进Java对象DirectByteBuffer中,当它的包装类被垃圾回收时,会调用相应的JNI方法释放本地内存,所以本地内存的释放也依赖于JVM中DirectByteBuffer对象的回收。

 

由于垃圾回收本身成本较高,一般JVM在堆内存未耗尽时,不会进行垃圾回收操作。我们知道在32位机器上,每个进程的最大可用内存为4G,用户可用内存在大概为3G左右,如果为堆分配过大的内存时,本地内存可用空间就会相应减少。当我们为堆分配较多的内存时,JVM可能会在相当长的时间内不会进行垃圾回收操作,从而本地内存不断分配,无法释放,最终导致OutOfMemoryError。

 

四、总结:

1、堆缓冲区的性能已经相当高,若无必要,使用堆缓冲区足矣。若确实有提升性能的必要时,再考虑使用本地缓冲区。

2、为JVM分配堆内存时,并不是越大越好,堆内存越大,本地内存就越小,根据具体情况决定,主要针对32位机器,64位机器上不存在该问题。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

public class NioServer{

    public static void main(String[] args) throws IOException {
        DatagramChannel channel=DatagramChannel.open();
        channel.configureBlocking(false);
        StringBuilder builder=new StringBuilder();
        builder.append("hello").append("nihao").append("one").append("two").append("three");
        ByteBuffer buffer=ByteBuffer.allocate(2048);
        buffer.clear();
        buffer.put(builder.toString().getBytes());
        buffer.flip();
        int num=channel.send(buffer,new InetSocketAddress("127.0.0.1",9090));
        System.out.println(num);
        channel.close();
    }

}
import java.io.*;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class NioClient {
    private static ScheduledExecutorService service= Executors.newScheduledThreadPool(1);
    public static void main(String[] args) throws IOException {
        //创建选择器
        Selector selector=Selector.open();
        //创建通道
        DatagramChannel channel=DatagramChannel.open();
        //设置为非阻塞模式
        channel.configureBlocking(false);
        DatagramSocket socket=channel.socket();
        socket.bind(new InetSocketAddress(9090));
        //将通道注册至selector,监听只读消息(此时服务端只能读数据,无法写数据)
        channel.register(selector,SelectionKey.OP_READ);
        ByteBuffer buffer=ByteBuffer.allocate(2048);
        final int num_size=10;
        service.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try{
                    int num=selector.select();
                    if(num>0){
                        Iterator<SelectionKey> readkeys=selector.selectedKeys().iterator();
                        while (readkeys.hasNext()){
                            SelectionKey key=readkeys.next();
                            readkeys.remove();
                            try{
                                if(key.isReadable()){
                                    DatagramChannel dc= (DatagramChannel) key.channel();
                                    InetSocketAddress client= (InetSocketAddress) dc.receive(buffer);
                                    key.interestOps(SelectionKey.OP_READ);
                                    if(buffer.position()>=num_size){
                                        buffer.flip();
                                        DataInputStream dis=new DataInputStream(new ByteArrayInputStream(buffer.array()));
                                        BufferedReader d=new BufferedReader(new InputStreamReader(dis,"GBK"));
                                        String str=d.readLine();
                                        if(str!=null){
                                            dosive(str);
                                        }
                                        buffer.clear();
                                    }

                                }
                            }catch(Exception e){
                                try {
                                    if (key != null) {
                                        key.cancel();
                                        key.channel().close();
                                    }
                                } catch (ClosedChannelException cex) {
                                    System.out.println(cex);
                                }
                            }
                        }
                    }
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        },0L,2L,TimeUnit.MINUTES);


    }
    public static void dosive(String key){
        System.out.println(key);
    }
}

 

posted @ 2019-06-26 15:38  烟雨观春柳  阅读(609)  评论(0)    收藏  举报