首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

在Java中为JOGL释放直接缓冲区本机内存

在Java中,为JOGL释放直接缓冲区本机内存,可以使用BufferUtils类提供的方法来完成。BufferUtils是JOGL库中的一个工具类,用于处理缓冲区相关的操作。

要释放直接缓冲区本机内存,可以按照以下步骤进行:

  1. 首先,确保你已经创建了一个直接缓冲区。直接缓冲区是一种在本机内存中分配的缓冲区,可以直接与本机的硬件设备进行交互,效率较高。
  2. 使用BufferUtils类的destroyDirectBuffer()方法来释放直接缓冲区的本机内存。该方法接受一个Buffer对象作为参数,可以是ByteBufferShortBufferIntBuffer等缓冲区类型。

下面是一个示例代码:

代码语言:java
复制
import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.GLProfile;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.BufferUtils;

import javax.swing.*;
import java.nio.FloatBuffer;

public class JOGLExample implements GLEventListener {

    private FloatBuffer vertexBuffer;

    @Override
    public void init(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();

        // 创建一个顶点数组
        float[] vertices = {
                -0.5f, -0.5f, 0.0f,
                0.5f, -0.5f, 0.0f,
                0.0f, 0.5f, 0.0f
        };

        // 创建一个直接缓冲区
        vertexBuffer = BufferUtils.newDirectFloatBuffer(vertices.length);
        vertexBuffer.put(vertices);
        vertexBuffer.flip();

        // 设置顶点数组
        gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
        gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertexBuffer);
    }

    @Override
    public void display(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();

        // 渲染代码
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl.glDrawArrays(GL.GL_TRIANGLES, 0, 3);
        gl.glFlush();
    }

    @Override
    public void dispose(GLAutoDrawable drawable) {
        GL2 gl = drawable.getGL().getGL2();

        // 释放直接缓冲区本机内存
        BufferUtils.destroyDirectBuffer(vertexBuffer);
    }

    @Override
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        // 窗口大小改变时的处理
    }

    public static void main(String[] args) {
        // 创建JOGL窗口
        GLProfile profile = GLProfile.get(GLProfile.GL2);
        GLCapabilities capabilities = new GLCapabilities(profile);
        GLCanvas canvas = new GLCanvas(capabilities);

        JOGLExample example = new JOGLExample();
        canvas.addGLEventListener(example);

        JFrame frame = new JFrame("JOGL Example");
        frame.getContentPane().add(canvas);
        frame.setSize(800, 600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

在上述示例代码中,dispose()方法中调用了BufferUtils.destroyDirectBuffer()方法来释放直接缓冲区的本机内存。

这是一个简单的JOGL示例,用于绘制一个三角形。在实际应用中,你可以根据自己的需求创建和使用直接缓冲区,并在不需要时及时释放内存,以避免内存泄漏和资源浪费。

腾讯云相关产品和产品介绍链接地址:

以上是腾讯云提供的一些与云计算相关的产品,可以根据具体需求选择适合的产品来支持和扩展你的应用。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

java 语音聊天室

另外他还解释了缓冲区直接缓冲区之间的区别,以及如何应用缓冲区来高速缓存数据以达到提高性能的目的。但具有大缓冲区的磁盘实际上可能和中间缓冲区一样减慢性能。...讨论了 Java 堆和系统内存、MappedByteBuffer、各种 Java 方法的性能以及如何使用强制方法写入数据后, Syrus 展示了一张显示基准测试的幻灯片,比较了使用 Java 编程语言和使用...缓冲区 16K 时, 当本地代码才进行到 78% 时,Java 已经 100% 拷贝完成。Cyrus 也讨论了相对于非直接缓冲区而言,使用直接缓冲区的优势和益处。...玩家分配了 CPU 和内存,而不是空间。 。整个世界的状态以数据的形式存在于持久的、容错的数据仓库。 。减少了编程复杂性。 。处理层是事件驱动的,并且完全无状态。 。...https://joal.dev.java.net/ JOGL -- JOGL 提供了对 OpenGL 1.4 规范和几乎全部厂商扩展的 APi 的完全访问,并且集成了 AWT 和 Swing 窗口部件集

9K71

深入浅出 Java FileChannel 的堆外内存使用丨社区分享

) 关于 堆外内存 堆外内存直接从操作系统中分配的内存,它不是 JVM 运行时数据区的一部分,也不是 JVM 规范定义的内存区域,因此不受 Java 堆大小的限制,但仍然会受到本机内存的大小及处理器寻址空间的限制...可以 Java VisualVM 安装插件 Buffer Pools 来监控堆外内存。 为什么 FileChannel 要使用堆外内存?...FileChannel 使用 Native 函数库直接分配堆外内存,然后通过一个存储 Java 堆里面的 DirectByteBuffer 对象作为这块内存直接引用进行操作,从而避免了 Java...实例对象被回收时, Reference-Handler 线程操作,会调用 Cleaner 的 clean 方法根据创建 Cleaner 时传入的 Deallocator 来进行堆外内存释放(即调用...释放或缓存临时 DirectByteBuffer: 这里的 TEMP_BUF_POOL_SIZE 被设置 IOUtil.IOV_MAX ,该值与操作系统有关, Linux 系统上默认值是 1024

1.3K20
  • jzy3D从入门到弃坑

    选择0.9版使用,而不要使用其他版本 总体来说这个软件坑就坑不告诉你怎么用,教程付费,包括安装都不告诉你~ 实属老赖 ?...但是这个lib包不能够直接使用,其需要JOGL以及各种依赖包 解决方案 直接看他自己的运行案例中有什么包就好了 ? ?...下载所有例子,注意其示例是Eclipse的,我喜欢IDEA+Maven,因此,直接将下载文件解压,并将其lib的所有包全部提取出来都作为依赖文件添加进IDEA,简单粗暴 ?...: javax/media/opengl/GLCapabilitiesImmutable 错误 直接向POM文件添加上正确的OPENGL版本 “https://jogamp.org/wiki/index.php...然后将lib的所有包转换成全局的lib包,而不仅仅是libraries ? 结果 自己瞎掰瞎试的,毕竟只是本人不是专业java调包侠 ^ _ ^ ?

    63020

    Flink 1.14.0 内存优化你不懂?跟着土哥走就对了(万字长文+参数调优)

    1.1 JVM 数据运行区 Java 虚拟机执行 Java 程序的过程中会把它在主存管理的内存部分划分成多个区域,每个区域存放不同类型的数据。...某次 GC 过程,如果发现仍然又放不下的对象,就将这些对象放入老年代内存里去。 老年代:大对象以及长期存活的对象直接进入老年代。...Java NIO 包中提供了 ByteBuffer 类,使用下面的方式,可以直接开辟指定大小的堆外内存,如下图为创建 128M 堆外内存。...如果没有这个缓冲区,那么生产者或消费者会消耗大量时间等待下游拿数据和上游发数据的环节上。加上这个缓冲区,生产者和消费者解耦开,任何一方短时间内的抖动理论上对另一方的数据处理都不会产生太大影响。...配置如下: 5.3.2 RocksDB 状态后端 该 EmbeddedRocksDBStateBackend 使用本机内存。默认情况下,RocksDB 设置本机内存分配限制为托管内存的大小。

    5.4K42

    Java NIO详解

    直接缓冲区和非直接缓冲区直接缓冲区 通过:static ByteBuffe allocate(int capacity)创建指定大小的缓冲区JVM内存创建,每次调用基础操作系统的一个本机...;JVM内存外开辟空间,每次调用基础操作系统的一个本机IO之前或者之后,虚拟机都会避免将缓冲区的内容复制到中间缓冲区(或者从缓冲区复制内容),缓冲区的内容驻留在屋里内存,少一次复制过程,如果需要循环使用缓冲区...直接缓冲区和非直接缓冲区的区别 字节缓冲区要么是直接的,要么是非直接的。如果直接字节缓冲区,则 Java 虚拟机会尽最大努力直接在此缓冲区上执行本机 I/O 操作。...也就是说,每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区(或从中间缓冲区复制内容)。...Java 平台的实现有助于通过 JNI 从本机代码创建直接字节缓冲区

    1.1K10

    JDK19都出来了~是时候梳理清楚JDK的各个版本的特性了【JDK16特性讲解】

    不幸的是,Java API 并没有为访问外部内存提供令人满意的解决方案: Java 1.4 引入的ByteBufferAPI允许创建直接字节缓冲区,这些缓冲区堆外分配,因此允许用户直接Java...但是,直接缓冲区是有限的。例如,无法创建大于 2 GB 的直接缓冲区,因为 ByteBufferAPI 使用 int基于索引的方案。...此外,使用直接缓冲区可能很麻烦,因为与它们相关联的内存释放留给垃圾收集器;也就是说,只有垃圾收集器认为直接缓冲区不可访问后,才能释放相关内存。...可以从各种来源创建内存段,例如本机内存缓冲区内存映射文件、Java 数组和字节缓冲区直接或基于堆)。...100 字节的本机内存缓冲区相关联的内存段。

    1.6K50

    「音视频直播技术」JNI编程常见问题

    复制:具有副本的缓冲区释放;对它的任何更改都会丢失。 检查isCopy标志的原因之一,是更改数组后知道是否需要使用JNI_COMMIT参数调用Release。...常见的错误,是认为如果 *isCopyfalse,则可以跳过Release调用。如果没有分配复制缓冲区,则原始内存必须被固定,并且不能被垃圾收集器移动。...某些实现,GetByteArrayElements和GetPrimitiveArrayCritical将返回实际指向托管堆中原始数据的指针,但另一方面,它将在本机堆上分配一个缓冲区并复制数据。...另一种方法是将数据存储直接字节缓冲区。这些可以使用java.nio.ByteBuffer.allocateDirect或JNI NewDirectByteBuffer函数创建。...(例如,如果数据最终被传递给byte[]的函数,那么直接ByteBuffer中进行处理可能是不明智的。) 如果基于上面的两点仍然判断不出来,请使用直接字节缓冲区

    1.6K20

    JVM-直接内存

    Java ,我们使用java.nio 包和java.lang.System类的arraycopy()方法等来操作直接内存。...为什么需要 JVM 直接内存? 某些场景下,使用 JVM 直接内存可以带来一些优势: 性能提升:JVM 直接内存的分配和释放效率更高,操作速度比 Java 堆更快。...JVM 直接内存的实现原理主要涉及 Java 的 NIO 库和本地内存管理。 Java ,可以通过ByteBuffer类来操作 JVM 直接内存。...分配在直接内存上的ByteBuffer 称为直接缓冲区(Direct Buffer)。 直接缓冲区分配时会调用本地系统的接口进行内存分配,生成一个本地内存地址。...它具有高性能、避免堆内存限制和直接 I/O 操作的优点,但需要手动管理内存释放,且内存管理较为复杂。使用直接内存时,需要注意内存释放和避免分配过多的直接内存。 本文由 mdnice 多平台发布

    54730

    Java性能优化系列集锦

    4 、并发控制方法: ( 1 )、 Java 内存模型与 Volatile : Java ,每一个线程有一块工作内存区,其中存放着被所有线程共享的主内存的变量的值的拷贝。...( 7 )、本机直接内存( Direct Memory )  JDK1.4 中新加入了 NIO 类,引入一种基于渠道与缓冲区的 I/O 方式,它可以通过本机 Native 函数库直接分配本机内存,然后通过一个存储...这样能在一些场景显著提高性能,因为避免了 Java 对和本机来回复制数据。...类加载通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存的大小类加载完成后便可完全确定。对象分配空间的任务等价于把一块确定大小的内存从 Java 堆划分出来。      ...比如,比如在 Java 代码采用了 JNI 操作,即在内存分配时,采用了类似 C 语言中的 malloc 函数来分配内存,而且没有调用free 函数进行释放

    71350

    感悟优化——Netty对JDK缓冲区内存池零拷贝改造

    直接内存和堆内存 ---- 首先先讲一下这里面需要用的基础知识,JVM 内存可分为两大块,一个是堆内存,一个是直接内存。...直接内存: JVM使用Native函数堆外分配内存,之后通过Java的DirectByteBuffer对象作为这块内存的引用进行操作。直接内存不会受到Java堆的限制,只受本机内存影响。...Java的GC只会在老年区满了触发Full GC时,才会去顺便清理直接内存的废弃对象。 JDK原生缓冲区ByteBuffer ---- NIO,所有数据都是用缓冲区处理的。...JVM直接内存,存在堆内存其实就是DirectByteBuffer类,它本身其实很小,真的内存堆外,这里是映射关系。...2.handler处理业务的时候,使用内存池中的堆内存 还有一点值得注意的是:使用完内存池中的ByteBuf,一定要记得释放,即调用release(): // 在内存池中申请 直接内存

    1.8K20

    Netty技术全解析:PooledUnsafeDirectByteBuf详解

    Netty这个高性能的网络编程框架,PooledUnsafeDirectByteBuf是一个关键的组件,它代表了基于内存池的、非安全的(即不使用Java的Unsafe类进行内存操作的)、直接的(即分配在...它通过减少内存分配和释放的开销、降低内存复制成本以及对GC压力小等特点,高性能网络编程提供了有力的支持。同时,Netty 通过避免直接使用 Unsafe 类来确保实现的稳定性和安全性 1....内存池技术 Netty 使用内存池技术来减少内存分配和释放的开销,特别是高并发场景下。...例如,readBytes方法用于从缓冲区读取字节数据,writeBytes方法用于向缓冲区写入字节数据。这些方法内部会使用Java的NIO库(如ByteBuffer)来实际进行数据的读写。...实际应用,我们可以根据具体的需求选择适当的缓冲区实现,以达到最佳的性能和内存使用效果。

    36410

    Java NIO使用及原理分析(三)

    或者我们也可以直接将一个现有的数组,包装为缓冲区对象,如下示例代码所示: [java] view plain copy print?...NIO,除了可以分配或者包装一个缓冲区对象外,还可以根据现有的缓冲区对象来创建一个子缓冲区,即在现有缓冲区上切出一片来作为一个新的缓冲区,但现有的缓冲区与创建的子缓冲区底层数组层面上是数据共享的...直接缓冲区 直接缓冲区加快I/O速度,使用一种特殊方式其分配内存缓冲区,JDK文档的描述:给定一个直接字节缓冲区Java虚拟机将尽最大努 力直接对它执行本机I/O操作。...也就是说,它会在每一次调用底层操作系统的本机I/O操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区 或者从一个中间缓冲区拷贝数据。...内存映射文件I/O是通过使文件的数据出现为 内存数组的内容来完成的,这其初听起来似乎不过就是将整个文件读到内存,但是事实上并不是这样。一般来说,只有文件实际读取或者写入的部分才会映射到内存

    58420

    面试系列之-rocketmq零拷贝原理

    传统版本 一台服务器把本机磁盘文件的内容发送到客户端,一般分为两个步骤: read:读取本地文件内容; write:将读取的内容通过网络发送出去; 传统的WEB服务器收到请求后,从磁盘读取数据,然后将数据写到网卡...,CPU直接将内核缓冲区的数据拷贝到socket缓冲区; DMA把数据从内核的socket缓冲区拷贝到网卡的缓冲区里; 细节过程 应用成都发起mmap系统调用,CPU从用户态切换到内核态; CPU建立内核空间与用户空间的内存地址映射...,上下文从用户态转向内核态; CPU直接将内核缓冲区的数据拷贝到socket缓冲区; DMA把数据从网络驱动的socket缓冲区拷贝到网卡的缓冲区,上下文从内核态切换回用户态,sendfile()调用返回...,这样网卡的SG-DMA控制器就可以直接将内核缓存的数据拷贝到网卡的缓冲区里,此过程不需要将数据从操作系统内核缓冲区拷贝到socket缓冲区,这样就减少了一次数据拷贝; 变化后发生了2次用户态与内核态的上下文切换和...,该参数的意思是告诉系统内核,这块内存一会儿就会用到,于是系统就会提前加载被映射的文件数据到内存,这样就不会在需要使用的时候才去读取磁盘,影响性能; 刷盘时的map调用 实际存储消息的时候,无论是使用堆外内存还是直接使用

    1.3K40

    ASP.NET Core 内存管理和垃圾回收 (GC)

    置于堆的对象归类 3 个代系之一:0、1 或 2。 代系可确定 GC 尝试应用不再引用的托管对象上释放内存的频率。 编号较低的代系会更加频繁地进行 GC。...当 ASP.NET Core 应用启动时,GC 会: 初始堆段保留一些内存。 在运行时加载时提交一小部分内存。 进行以上内存分配是出于性能方面的原因。 性能优势来自连续内存的堆段。...调查内存泄漏时非常有用。 进行调查时会验证 GC 是否从内存删除了所有无关联对象,以便可以度量内存。...IMemoryCache 接口 表示未序列化其值的本地内存缓存。 本机内存 某些 .NET Core 对象依赖于本机内存。 GC 无法回收本机内存。...使用本机内存的 .NET 对象必须使用本机代码进行释放。 .NET 提供了IDisposable 接口,使开发人员能够释放本机内存

    34430

    庖丁解牛:NIO核心概念与机制详解 03 _ 缓冲区分配、包装和分片

    这里我们可以看到 如何创建不同类型的缓冲区以达到不同的目的, 如可保护数据不被修改的 只读 缓冲区,和 直接映射到底层操作系统缓冲区直接 缓冲区。 最后介绍如何在 NIO 创建内存映射文件。...直接和间接缓冲区 (allocateDirect) 另一种有用的 ByteBuffer 是直接缓冲区直接缓冲区加快 I/O 速度,而以一种特殊的方式分配其内存缓冲区。...实际上,直接缓冲区的准确定义是与实现相关的。 Oracle 的文档是这样描述直接缓冲区的: 给定一个直接字节缓冲区Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作。...一般来说,只有文件实际读取或者写入的部分才会送入(或者 映射 )到内存内存映射并不真的神奇或者多么不寻常。现代操作系统一般根据需要将文件的部分映射内存的部分,从而实现文件系统。...Java 内存映射机制不过是底层操作系统可以采用这种机制时,提供了对该机制的访问。 尽管创建内存映射文件相当简单,但是向它写入可能是危险的。

    20430

    ASP.NET Core 内存管理和垃圾回收 (GC)

    置于堆的对象归类 3 个代系之一:0、1 或 2。 代系可确定 GC 尝试应用不再引用的托管对象上释放内存的频率。 编号较低的代系会更加频繁地进行 GC。...当 ASP.NET Core 应用启动时,GC 会: 初始堆段保留一些内存。 在运行时加载时提交一小部分内存。 进行以上内存分配是出于性能方面的原因。 性能优势来自连续内存的堆段。...调查内存泄漏时非常有用。 进行调查时会验证 GC 是否从内存删除了所有无关联对象,以便可以度量内存。...IMemoryCache 接口 表示未序列化其值的本地内存缓存。 本机内存 某些 .NET Core 对象依赖于本机内存。 GC 无法回收本机内存。...使用本机内存的 .NET 对象必须使用本机代码进行释放。 .NET 提供了IDisposable 接口,使开发人员能够释放本机内存

    45320

    面试官:从 MySQL 数据库里读取 500w 数据行进行处理,应该怎么做更效益化?

    接口 1 MySql JDBC 大数据量操作 整篇文章以大数据量操作为议题,通过开发过程的需求引出相关知识点 迁移数据 导出数据 批量处理数据 一般而言笔者认为 Java Web 程序里,能够被称为大数据量的...LIMIT 深分页优化情况下,线上数据库服务器就凉了,亦或者你能等个几天时间检索数据 1.2 流式查询 如果你正在使用具有大量数据行的 ResultSet,并且无法 JVM 其分配所需的内存堆空间...操作上,当缓冲区有数据时,程序会被唤醒进而将缓冲区数据读取到 JVM 内存 MySQL Server 会不断发送数据,JDBC 不断读取缓冲区数据到 Java 内存,虽然此时数据已到 JDBC 所在程序本地...,一般情况不必担心本机内核无数据消费(除非 MySQL 服务端传递来的数据,客户端不做任何业务逻辑,拿到数据直接放弃,会发生客户端消费比服务端超前的情况) 看起来,流式要比游标的方式更好一些,但是事情往往不像表面上那么简单...但是这两种方式会占用数据库连接,使用不会释放,所以线上针对大数据量业务用到游标和流式操作,一定要进行并发控制 另外针对 JDBC 原生流式查询,Mybatis 也进行了封装,虽然会慢一些,但是 功能以及代码的整洁程度会好上不少

    2.1K30

    Android JNI学习(四)——JNI的常用方法的中文API

    直接缓冲区的内容可能存在于普通的垃圾回收器以外的本地内存。有关直接缓冲区的信息,可以参考 NIO和java.nio.ByteBuffer类的规范。...JDK/JRE 1.4引入了新的JNI函数,允许检查和操作做直接缓冲区 NewDirectByteBuffer GetDirectBufferAddress GetDirectBufferCapacity...如果JVM不支持JNI访问直接缓冲区,也会返回NULL 异常: 如果缓冲区分配失败,则返回OutOfMemoryError (二) 返回直接缓冲区对象的初始地址 void* GetDirectBufferAddress...(JNIEnv *env,jobject buf); 获取并返回java.nio.Buffer的内存初始地址 该函数允许Native代码通过直接缓冲区对象访问Java代码的同一内存区域 参数解释:...(三) 返回直接缓冲区对象的内存容量 jlong GetDirectBufferCapacity(JNIEnv *env,jobject buf); 获取并返回java.nio.Buffer的内存容量

    3.3K20

    JVM-直接内存(Direct Memory)

    直接内存(Direct Memory) 直接内存Java堆之外的,直接向系统申请的内存空间,所以直接内存不是虚拟机的一部分,也不是《Java虚拟机规范》定义的内存区域,也有可能导致OOM。...非直接缓存区 jdk1.4之前,java的对象与系统之间的交互如下图,先从JVM需要从用户态切换到内核态时,这样的话读取或写入一份数据需要经历四个步骤:jvm切换到内核态缓冲区读取->操作系统将数据拷贝用户缓冲区...直接缓存区 直接内存也称直接缓存区,主要是解决一个java读取慢的问题,jdk1.4以后jvm 引入了NIO操作系统划出了一块直接的缓存区可以直接java访问。就是所称的零拷贝。...代码实现 非直接缓冲区 /** * @author: csh * @Date: 2021/5/8 18:49 * @Description:非直接缓存冲(堆内存) */ public class...释放前 ? ? 释放后 ? ?

    1.5K20

    Netty 源码深度解析(九) - 编码

    时,就不需要自己手动释放了) 5 传播数据 //112 如果buf写入了数据,就把buf传到下一个节点,直到 header 节点 6 释放内存 //115 否则,释放buf,将空数据传到下一个节点...// 120 如果当前节点不能处理传入的对象,直接扔给下一个节点处理 // 127 当bufpipeline处理完之后,释放 Encoder处理传入的Java对象 判断当前Handler...时,无需自己手动释放) 如果buf写入了数据,就把buf传到下一个节点,否则,释放buf,将空数据传到下一个节点 最后,当bufpipeline处理完之后,释放节点 总结就是,Encoder节点分配一个...确保该方法的调用是reactor线程 然后,调用 filterOutboundMessage(),将待写入的对象过滤,把非ByteBuf对象和FileRegion过滤,把所有的非直接内存转换成直接内存...Socket缓冲区,写的过程与并发编程类似,用自旋锁保证写成功 netty缓冲区的ByteBufDirectByteBuf 如何把对象变成字节流,最终写到socket底层?

    38770
    领券