看完你就能知道Java 线程栈虚拟内存在Windows 中是如何释放的

实战篇:双管齐下优化开发环境——Windows虚拟内存与IDEA JVM内存调优指南 本文详细介绍了如何通过优化Windows虚拟内存和IDEA JVM内存配置来提升开发环境的性能和稳定性。针对不同项目规模,提供了具体的JVM参数配置方案,并分享了Windows虚拟内存的手动设置步骤和进阶技巧,帮助开发者有效解决内存不足导致的卡顿问题。 阅读详情

本文研究 hotspot 虚拟机运行在 Win32 程序时: Java 线程的创建到操作系统线程创建的内存分配过程 Win32 线程栈虚拟内存释放过程 如果你不了解 win32 的内存管理相关知识,可以先看 Windows 内存管理知识总结

Java 线程 & JVM & 操作系统的关系

​

结论

 

Java 的线程对象实际是个空壳,真正逻辑在 native 层 Java 层通过 JNI 调用到 jvm.cpp,会创建一个 C++ Thread 对象 C++ Thread 对象会持有一个 OSThread 对象 OSThread 会持有具体操作系统创建线程的 HANDLE 想最终释放线程栈占进程的虚拟内存,需要释放 HANDLE

下面会按照一条 Java 线程从创建到销毁、内存从分配到释放的顺序分析核心代码。

Java 线程内存分配

Java new Thead -> thread.start()

/** * Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * <code>start</code> method) and the other thread (which executes its * <code>run</code> method). * <p> * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. * * @exception IllegalThreadStateException if the thread was already * started. * @see #run() * @see #stop() */ public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0(); 复制代码

  • Java 层调用到 start() 时,实际是通过 JNI 调用了 native 方法 start0

Thread JNI 方法注册表

// jdk/src/share/native/java/lang/Thread.c:43 static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, {"isAlive", "()Z", (void *)&JVM_IsThreadAlive}, {"suspend0", "()V", (void *)&JVM_SuspendThread}, {"resume0", "()V", (void *)&JVM_ResumeThread}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, {"yield", "()V", (void *)&JVM_Yield}, {"sleep", "(J)V", (void *)&JVM_Sleep}, {"currentThread", "()" THD, (void *)&JVM_CurrentThread}, {"countStackFrames", "()I", (void *)&JVM_CountStackFrames}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted}, {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, {"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName}, }; // jdk/src/share/javavm/export/jvm.h:254 JNIEXPORT void JNICALL JVM_Interrupt(JNIEnv *env, jobject thread); 复制代码

  • 上述代码标明了 Java 方法和 C++ 函数的映射关系

JVM_StartThread

start0() 实际调用到了 JVM_StartThread 函数中

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) JavaThread *native_thread = NULL; { // Ensure that the C++ Thread and OSThread structures aren't freed before // we operate. MutexLocker mu(Threads_lock); // Since JDK 5 the java.lang.Thread threadStatus is used to prevent // re-starting an already started thread, so we should usually find // that the JavaThread is null. However for a JNI attached thread // there is a small window between the Thread object being created // (with its JavaThread set) and the update to its threadStatus, so we // have to check for this if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) { throw_illegal_thread_state = true; } else { // We could also check the stillborn flag to see if this thread was already stopped, but // for historical reasons we let the thread detect that itself when it starts running jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread)); // Allocate the C++ Thread structure and create the native thread. The // stack size retrieved from java is signed, but the constructor takes // size_t (an unsigned type), so avoid passing negative values which would // result in really large stacks. size_t sz = size > 0 ? (size_t) size : 0; native_thread = new JavaThread(&thread_entry, sz); // At this point it may be possible that no osthread was created for the // JavaThread due to lack of memory. Check for this situation and throw // an exception if necessary. Eventually we may want to change this so // that we only grab the lock if the thread was created successfully - // then we can also do this check and throw the exception in the // JavaThread constructor. if (native_thread->osthread() != NULL) { // Note: the current thread is not being used within "prepare". native_thread->prepare(jthread); } } } if (native_thread->osthread() == NULL) { // No one should hold a reference to the 'native_thread'. delete native_thread; if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, "unable to create new native thread"); } THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "unable to create new native thread"); } Thread::start(native_thread); JVM_END 复制代码

  • JavaThread 是最开始图中的 C++ Thread 一方面是对操作系统线程做封装,可以看到其持有了 osthread 另一方面是绑定了 Java 层的 thread 对象 jthread,通过 thread_entry 调用到了 Java 层 Thread 类的 run()

  • C++ Thread 的对象内存会分配到 Heap 中

new JavaThread

JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : Thread() #if INCLUDE_ALL_GCS , _satb_mark_queue(&_satb_mark_queue_set), _dirty_card_queue(&_dirty_card_queue_set) #endif // INCLUDE_ALL_GCS { if (TraceThreadEvents) { tty->print_cr("creating thread %p", this); } initialize(); _jni_attach_state = _not_attaching_via_jni; set_entry_point(entry_point); // Create the native thread itself. // %note runtime_23 os::ThreadType thr_type = os::java_thread; thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : os::java_thread; os::create_thread(this, thr_type, stack_sz); _safepoint_visible = false; // The _osthread may be NULL here because we ran out of memory (too many threads active). // We need to throw and OutOfMemoryError - however we cannot do this here because the caller // may hold a lock and all locks must be unlocked before throwing the exception (throwing // the exception consists of creating the exception object & initializing it, initialization // will leave the VM via a JavaCall and then all locks must be unlocked). // // The thread is still suspended when we reach here. Thread must be explicit started // by creator! Furthermore, the thread must also explicitly be added to the Threads list // by calling Threads:add. The reason why this is not done here, is because the thread // object must be fully initialized (take a look at JVM_Start) } 复制代码

  • 将 entry_point 保存在 C++ Thread 中

  • 执行 os::create_thread,真正线程由操作系统函数创建

os::create_thread

// Allocate and initialize a new OSThread bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { unsigned thread_id; // Allocate the OSThread object OSThread* osthread = new OSThread(NULL, NULL); if (osthread == NULL) { return false; } // Initialize support for Java interrupts HANDLE interrupt_event = CreateEvent(NULL, true, false, NULL); if (interrupt_event == NULL) { delete osthread; return NULL; } osthread->set_interrupt_event(interrupt_event); osthread->set_interrupted(false); thread->set_osthread(osthread); if (stack_size == 0) { switch (thr_type) { case os::java_thread: // Java threads use ThreadStackSize which default value can be changed with the flag -Xss if (JavaThread::stack_size_at_create() > 0) stack_size = JavaThread::stack_size_at_create(); break; case os::compiler_thread: if (CompilerThreadStackSize > 0) { stack_size = (size_t)(CompilerThreadStackSize * K); break; } // else fall through: // use VMThreadStackSize if CompilerThreadStackSize is not defined case os::vm_thread: case os::pgc_thread: case os::cgc_thread: case os::watcher_thread: if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); break; } } // Create the Win32 thread // // Contrary to what MSDN document says, "stack_size" in _beginthreadex() // does not specify stack size. Instead, it specifies the size of // initially committed space. The stack size is determined by // PE header in the executable. If the committed "stack_size" is larger // than default value in the PE header, the stack is rounded up to the // nearest multiple of 1MB. For example if the launcher has default // stack size of 320k, specifying any size less than 320k does not // affect the actual stack size at all, it only affects the initial // commitment. On the other hand, specifying 'stack_size' larger than // default value may cause significant increase in memory usage, because // not only the stack space will be rounded up to MB, but also the // entire space is committed upfront. // // Finally Windows XP added a new flag 'STACK_SIZE_PARAM_IS_A_RESERVATION' // for CreateThread() that can treat 'stack_size' as stack size. However we // are not supposed to call CreateThread() directly according to MSDN // document because JVM uses C runtime library. The good news is that the // flag appears to work with _beginthredex() as well. #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION #define STACK_SIZE_PARAM_IS_A_RESERVATION (0x10000) #endif HANDLE thread_handle = (HANDLE)_beginthreadex(NULL, (unsigned)stack_size, (unsigned (__stdcall *)(void*)) java_start, thread, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, &thread_id); if (thread_handle == NULL) { // perhaps STACK_SIZE_PARAM_IS_A_RESERVATION is not supported, try again // without the flag. thread_handle = (HANDLE)_beginthreadex(NULL, (unsigned)stack_size, (unsigned (__stdcall *)(void*)) java_start, thread, CREATE_SUSPENDED, &thread_id); } if (thread_handle == NULL) { // Need to clean up stuff we've allocated so far CloseHandle(osthread->interrupt_event()); thread->set_osthread(NULL); delete osthread; return NULL; } Atomic::inc_ptr((intptr_t*)&os::win32::_os_thread_count); // Store info on the Win32 thread into the OSThread osthread->set_thread_handle(thread_handle); osthread->set_thread_id(thread_id); // Initial thread state is INITIALIZED, not SUSPENDED osthread->set_state(INITIALIZED); // The thread is returned suspended (in state INITIALIZED), and is started higher up in the call chain return true; } 复制代码

  • 分配 OSThread 对象

  • CreateEvent(interrupt_event),信号量机制,具体参考learn.microsoft.com/en-us/windo…

  • 初始化 stack_size

  • 通过 _beginthreadex 创建 Windows 线程,并将 HANDLE 设置到 OSThread 中

C++ Thread 开始执行

上一节 _beginthreadex 的第 3 个参数表示 Windows 线程跑起来后要执行的函数地址

// Thread start routine for all new Java threads static unsigned __stdcall java_start(Thread* thread) { // Try to randomize the cache line index of hot stack frames. // This helps when threads of the same stack traces evict each other's // cache lines. The threads can be either from the same JVM instance, or // from different JVM instances. The benefit is especially true for // processors with hyperthreading technology. static int counter = 0; int pid = os::current_process_id(); _alloca(((pid ^ counter++) & 7) * 128); OSThread* osthr = thread->osthread(); assert(osthr->get_state() == RUNNABLE, "invalid os thread state"); if (UseNUMA) { int lgrp_id = os::numa_get_group_id(); if (lgrp_id != -1) { thread->set_lgrp_id(lgrp_id); } } // Install a win32 structured exception handler around every thread created // by VM, so VM can genrate error dump when an exception occurred in non- // Java thread (e.g. VM thread). __try { thread->run(); } __except(topLevelExceptionFilter( (_EXCEPTION_POINTERS*)_exception_info())) { // Nothing to do. } // One less thread is executing // When the VMThread gets here, the main thread may have already exited // which frees the CodeHeap containing the Atomic::add code if (thread != VMThread::vm_thread() && VMThread::vm_thread() != NULL) { Atomic::dec_ptr((intptr_t*)&os::win32::_os_thread_count); } return 0; } 复制代码

  • 调用了 thread -> run(),请继续看下一节

JavaThread::run

// The first routine called by a new Java thread void JavaThread::run() { // initialize thread-local alloc buffer related fields this->initialize_tlab(); // used to test validitity of stack trace backs this->record_base_of_stack_pointer(); // Record real stack base and size. this->record_stack_base_and_size(); // Initialize thread local storage; set before calling MutexLocker this->initialize_thread_local_storage(); this->create_stack_guard_pages(); this->cache_global_variables(); // Thread is now sufficient initialized to be handled by the safepoint code as being // in the VM. Change thread state from _thread_new to _thread_in_vm ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm); assert(JavaThread::current() == this, "sanity check"); assert(!Thread::current()->owns_locks(), "sanity check"); DTRACE_THREAD_PROBE(start, this); // This operation might block. We call that after all safepoint checks for a new thread has // been completed. this->set_active_handles(JNIHandleBlock::allocate_block()); if (JvmtiExport::should_post_thread_life()) { JvmtiExport::post_thread_start(this); } EventThreadStart event; if (event.should_commit()) { event.set_javalangthread(java_lang_Thread::thread_id(this->threadObj())); event.commit(); } // We call another function to do the rest so we are sure that the stack addresses used // from there will be lower than the stack base just computed thread_main_inner(); // Note, thread is no longer valid at this point! } 复制代码

  • record_stack_base_and_size 中会查询具体栈内存地址,分别调用 current_stack_base 和 current_stack_size

current_stack_base & current_stack_size

// os::current_stack_base() // // Returns the base of the stack, which is the stack's // starting address. This function must be called // while running on the stack of the thread being queried. address os::current_stack_base() { MEMORY_BASIC_INFORMATION minfo; address stack_bottom; size_t stack_size; VirtualQuery(&minfo, &minfo, sizeof(minfo)); stack_bottom = (address)minfo.AllocationBase; stack_size = minfo.RegionSize; // Add up the sizes of all the regions with the same // AllocationBase. while( 1 ) { VirtualQuery(stack_bottom+stack_size, &minfo, sizeof(minfo)); if ( stack_bottom == (address)minfo.AllocationBase ) stack_size += minfo.RegionSize; else break; } #ifdef _M_IA64 // IA64 has memory and register stacks // // This is the stack layout you get on NT/IA64 if you specify 1MB stack limit // at thread creation (1MB backing store growing upwards, 1MB memory stack // growing downwards, 2MB summed up) // // ... // ------- top of stack (high address) ----- // | // | 1MB // | Backing Store (Register Stack) // | // | / \ // | | // | | // | | // ------------------------ stack base ----- // | 1MB // | Memory Stack // | // | | // | | // | | // | \ / // | // ----- bottom of stack (low address) ----- // ... stack_size = stack_size / 2; #endif return stack_bottom + stack_size 复制代码

size_t os::current_stack_size() { size_t sz; MEMORY_BASIC_INFORMATION minfo; VirtualQuery(&minfo, &minfo, sizeof(minfo)); sz = (size_t)os::current_stack_base() - (size_t)minfo.AllocationBase; return sz; } 复制代码

  • 通过 Windows Api VirtualQuery 查询当前线程栈内存信息

  • JVM C++ Thread 记录 stack_size 的目的是为了处理 stack overflow 异常

thread_main_inner

void JavaThread::thread_main_inner() { assert(JavaThread::current() == this, "sanity check"); assert(this->threadObj() != NULL, "just checking"); // Execute thread entry point unless this thread has a pending exception // or has been stopped before starting. // Note: Due to JVM_StopThread we can have pending exceptions already! if (!this->has_pending_exception() && !java_lang_Thread::is_stillborn(this->threadObj())) { { ResourceMark rm(this); this->set_native_thread_name(this->get_thread_name()); } HandleMark hm(this); this->entry_point()(this, this); } DTRACE_THREAD_PROBE(stop, this); this->exit(false); delete this; } 复制代码

  • 重点是最后一行 delete this,会进一步调用 C++ Thread 的析构函数

thread_entry

// java.lang.Thread ////////////////////////////////////////////////////////////////////////////// // In most of the JVM Thread support functions we need to be sure to lock the Threads_lock // to prevent the target thread from exiting after we have a pointer to the C++ Thread or // OSThread objects. The exception to this rule is when the target object is the thread // doing the operation, in which case we know that the thread won't exit until the // operation is done (all exits being voluntary). There are a few cases where it is // rather silly to do operations on yourself, like resuming yourself or asking whether // you are alive. While these can still happen, they are not subject to deadlocks if // the lock is held while the operation occurs (this is not the case for suspend, for // instance), and are very unlikely. Because IsAlive needs to be fast and its // implementation is local to this file, we always lock Threads_lock for that one. static void thread_entry(JavaThread* thread, TRAPS) { HandleMark hm(THREAD); Handle obj(THREAD, thread->threadObj()); JavaValue result(T_VOID); JavaCalls::call_virtual(&result, obj, KlassHandle(THREAD, SystemDictionary::Thread_klass()), vmSymbols::run_method_name(), vmSymbols::void_method_signature(), THREAD); } 复制代码

  • run_method_name() 实际就是 Java Thread 的 run(),template(run_method_name, "run")

Java 线程内存释放

void JavaThread::thread_main_inner() { delete this; } 复制代码

  • 上一节提到 thread_main_inner 会调用 delete this 随后触发 C++ Thread 的析构函数,下面看看析构函数具体做了什么

Thread:~Thread()

Thread::~Thread() { // Reclaim the objectmonitors from the omFreeList of the moribund thread. ObjectSynchronizer::omFlush (this) ; EVENT_THREAD_DESTRUCT(this); // stack_base can be NULL if the thread is never started or exited before // record_stack_base_and_size called. Although, we would like to ensure // that all started threads do call record_stack_base_and_size(), there is // not proper way to enforce that. #if INCLUDE_NMT if (_stack_base != NULL) { address low_stack_addr = stack_base() - stack_size(); MemTracker::release_thread_stack(low_stack_addr, stack_size(), this); #ifdef ASSERT set_stack_base(NULL); #endif } #endif // INCLUDE_NMT // deallocate data structures delete resource_area(); // since the handle marks are using the handle area, we have to deallocated the root // handle mark before deallocating the thread's handle area, assert(last_handle_mark() != NULL, "check we have an element"); delete last_handle_mark(); assert(last_handle_mark() == NULL, "check we have reached the end"); // It's possible we can encounter a null _ParkEvent, etc., in stillborn threads. // We NULL out the fields for good hygiene. ParkEvent::Release (_ParkEvent) ; _ParkEvent = NULL ; ParkEvent::Release (_SleepEvent) ; _SleepEvent = NULL ; ParkEvent::Release (_MutexEvent) ; _MutexEvent = NULL ; ParkEvent::Release (_MuxEvent) ; _MuxEvent = NULL ; delete handle_area(); delete metadata_handles(); // osthread() can be NULL, if creation of thread failed. if (osthread() != NULL) os::free_thread(osthread()); delete _SR_lock; // clear thread local storage if the Thread is deleting itself if (this == Thread::current()) { ThreadLocalStorage::set_thread(NULL); } else { // In the case where we're not the current thread, invalidate all the // caches in case some code tries to get the current thread or the // thread that was destroyed, and gets stale information. ThreadLocalStorage::invalidate_all(); } CHECK_UNHANDLED_OOPS_ONLY(if (CheckUnhandledOops) delete unhandled_oops();) } 复制代码

  • 析构函数核心是调用了 os::free_thread

os::free_thread

// Free Win32 resources related to the OSThread void os::free_thread(OSThread* osthread) { assert(osthread != NULL, "osthread not set"); CloseHandle(osthread->thread_handle()); CloseHandle(osthread->interrupt_event()); delete osthread; } 复制代码

  • 重点是 CloseHandle,会释放 _beginthreadex 返回的 HANDLE,从而释放栈虚拟内存

操作系统面经大全——双非上岸阿里巴巴系列 双非本科,四面成功上岸阿里巴巴,在这里把自己整理的面经分享出来,希望能帮到大家。 文章目录1. 进程线程篇1. 进程、线程和协程2. 进程有哪几种状态?3. 进程间的通信的几种方式4. 线程有几种状态?5. 创建线程的方式6. Runnable、Callable、Future的异同7. run()和start()区别8. 守护、用户线程9. 查看线程运行的方法1. Windows2. linux3. Java10. 线程同步的方式11. 什么是上下文切换12. 如何减少上下文切换13. 线程调度策略14. 阅读详情

相关推荐

Java线程栈虚拟内存的释放时机

在Java中,线程是一种重要的执行单元,每个线程都有自己的栈空间,用于存储局部变量、方法调用信息等。在Java中,线程栈的虚拟内存是通过操作系统提供的内存管理机制进行分配和释放的。需要注意的是,线程的栈空间是在操作系统的虚拟内存中分配的,而不是Java虚拟机堆内存中。因此,线程栈的虚拟内存的释放时机通常是由操作系统管理的,Java虚拟机只负责通知操作系统线程已经执行完毕。在上面的示例中,当线程执行完毕后,"线程执行完毕"的消息会被打印出来,表示该线程已经执行完毕并且其栈空间已经被释放。方法启动线程的执行。

FcUnity的博客 312

Windows 中何时释放 Java 线程栈虚拟内存?

我正在参加「掘金·启航计划」如果你不了解 win32 的内存管理相关知识,可以先看。

BASK2311的博客 787

新手也能玩转!用Panosim+MATLAB/Simulink搭建你的第一个智能驾驶仿真场景(保姆级图文)

本文为新手提供了使用Panosim和MATLAB/Simulink搭建智能驾驶仿真场景的详细教程。从环境准备、基础场景创建到仿真模型构建,一步步指导用户完成第一个智能驾驶仿真项目,帮助零基础用户快速上手并体验智能驾驶仿真的乐趣。

weixin_29090089的博客 185

线程结束资源释放 线程终止

线程创建时,系统会分配给线程一些资源,我们可以看到的就是线程描述符,线程堆栈,在系统内部还会有更复杂的系统维护一些信息,在线程创建时,内核总会为其维护一些资源,比较理想的情况是线程运行结束后,释放系统资源和进程资源,包含线程返回值占用的内存,线程堆栈,寄存器状态等等,以备后来者的使用. 线程执行结束后释放资源的三种方法:   利用这些方法,我们可以避免线程退出时,系统资源仍

markman101---神即是道 道法自然 如来 1万+

手把手教你逆向分析某里系bx-ua参数(以225版本为例)

本文详细解析了某里系应用bx-ua参数的逆向工程实战方法,重点拆解其三层加密体系(二进制加密、字符替换和Base64编码)。通过工具链配置和实战案例,帮助开发者掌握逆向分析技巧,适用于合法合规的安全研究和加密技术评估场景。

weixin_33220360的博客 414

深度伪造检测实战:轻量级频域特征 pipeline 部署指南

深度伪造检测(deepfake detection)是多媒体内容安全的核心技术,其本质是通过建模生成式篡改在像素与频域留下的微弱异常痕迹。传统端到端大模型虽在标准数据集上表现优异,却因训练域与真实场景失配、计算开销高、缺乏可解释性而难以落地。本文聚焦于工程可复现的实践路径,提出‘人脸定位→频域伪造特征提取(Xception-FCN + Frequency-Aware Masking)→轻量分类决策’三段式解耦架构,兼顾精度、延迟与可解释性。该方案已在边缘设备(Jetson Orin NX)实现单帧<48ms

weixin_33437929的博客 310

一文读懂“Java性能调优之JVM GC(垃圾回收机制)”

定义:垃圾回收机制时指在Java语言的生命周期中,Java运行环境提供的一个系统的[垃圾回收器]线程,负责自动回收那些没有引用与之相连的对象所占用的内存。这种清除无用对象回收内存的行为就叫做垃圾回收。Java语言中一个显著的特点就是引入了垃圾回收机制,使其他编程语言最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有**“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,

H931053的博客 2266

Spring Authorization Server 实战指南:构建企业级 OAuth2/OIDC 授权中心

本文介绍了Spring Authorization Server作为构建企业级OAuth2/OIDC授权中心的解决方案。首先分析了传统认证模式的不足和OAuth2/OIDC协议的优势,然后详细讲解了如何从零搭建授权服务器,包括核心配置类、授权流程、数据库持久化等关键实现。文章还涵盖了资源服务器配置、客户端应用集成、安全最佳实践以及生产环境部署建议,完整呈现了Spring Authorization Server的核心功能和使用场景。通过本教程,开发者可以掌握统一认证授权系统的构建方法,为微服务架构提供安全可

qq_39476913的博客 783

Java中线程的六种状态

Java中线程的几种状态

m0_67683346的博客 1021

从涡流求解器到瞬态场:Maxwell 3D里无线充电仿真,选对求解器能省一半计算时间

本文深入探讨了Maxwell 3D在无线充电仿真中涡流求解器与瞬态求解器的选择策略,帮助工程师优化计算效率与精度。通过对比分析两种求解器的适用场景、性能差异及实战技巧,揭示如何根据频率特性、材料非线性和动态过程需求做出精准选择,可节省高达50%的计算时间。特别针对3D线圈模型的无线充电系统设计提供了专业建议。

weixin_26861329的博客 233

Java 类与对象 -- Java 语言的类与对象、构造器、static、final、包和 JAR

本篇文章我们将深入了解 Java 语言中的类与对象,探讨构造器、`static`、`final`、包和 JAR 文件。这些概念是面向对象编程的核心,对于理解和掌握 Java 至关重要。

栗筝i的博客 5039

Java的栈,堆,代码,静态存储区的存储顺序和位置

自己总结一下关于内存分区的基础知识:         先上图:     在VS测试中发现,字符常量区(只读数据区)在编译时候与静态数据区放在了一起。因此在静态数据区的几种类型没有独立的地址区域,都是按照定义的时间顺序进行分配。而动态内存区不同,堆和栈有独立的地址区域,具体介绍可以看下文。   1、 栈区(stack sagment):由编译器自动分配释放,存放函数的参数的值,局部变量的...

zhangbaoanhadoop的博客 6202

YOLOv11改进策略【小目标改进】| 添加专用于小目标的检测层 附YOLOv1~YOLOv11的检测头变化详解

在目标检测领域,小目标检测一直是一个具有挑战性的问题。算法以其高效快速的特点受到广泛关注,然而在面对小目标时,仍存在一些局限性。本文将介绍如何在中添加小目标检测层,以提高对小目标的检测能力。原始模型结构如下:详细内容参考:YOLOv11原始模型结构介绍 二、有效特征层对应的检测头类别 2.1 P3/8 - small检测头 原始模型中的对应的检测头主要用于检测相对较小的目标。其特征图大小相对较大,空间分辨率较高。 适合检测尺寸大概在到像素左右的目标。 2.2 P4/16 - medium检测头 这个检

Limiiiing的博客 8804

JAVA线程问题

1.进程中创建线程的限制 默认情况下,一个线程的栈要预留1M的内存空间,而一个进程中可用的内存空间只有2G,默认分配一个线程的堆栈大小是1MB,所以理论上一个进程中最多可以开2048个线程,但是内存当然不可能完全拿来作线程的栈,所以实际数目要比这个值要小。 实际上这个用户方式的2GB并不会全部用作线程堆栈。首先程序的代码和数据、进程环境块、线程环境块、空指针区域等等也需要占用一定的地...

baishikele971719623的博客 195

超声波传感器(CHx01) 学习笔记 Ⅲ - I2C读写操作

CH-x01 通过 2 线 I2C 协议与主机处理器通信

nicole088的专栏 1740

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

面试题思考:堆区和栈区的区别(内存)

文章目录主要区别:详细说明:1.申请方式不同:栈区(stack):堆区(heap):2.申请后系统的响应不同:栈区(stack):堆区(heap):3.申请大小的限制不同:栈区(stack):堆区(heap):4.申请效率的比较:栈区(stack):堆区(heap):5.堆和栈中的存储内容栈区(stack):堆区(heap): 主要区别: 1、栈区(stack) 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap) 一般由程序员分配释放,若程序

华山栈道的博客 1829

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

服务器如何合理设置虚拟内存,服务器虚拟内存设置多大合适

服务器虚拟内存设置多大合适 内容精选换一换开启弹性云服务器的虚拟内存后,会导致硬盘I/O性能下降,因此,平台提供的Windows弹性云服务器默认未配置虚拟内存。如果弹性云服务器内存不足,建议通过“变更规格”操作来扩大内存。如果业务需要,必须开启虚拟内存,请参见本节内容进行配置。内存使用率已经非常高,同时I/O性能也不是很好的情况下,如果配置虚拟内存会起到反向效果:不仅系统性能提升Windows场景...

weixin_35736086的博客 1652

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Java程序报内存不足?可能是Windows虚拟内存pagefile.sys在捣鬼

本文深入解析Java程序内存不足问题,揭示Windows虚拟内存pagefile.sys的关键影响。通过诊断工具使用、优化方案配置和实战经验分享,帮助开发者有效解决内存瓶颈,提升开发效率。特别针对IDE环境和多磁盘配置提供专业建议。

weixin_30631587的博客 404

服务器虚拟内存能否设置小,服务器虚拟内存能否设置小

服务器虚拟内存能否设置小 内容精选换一换开启弹性云服务器的虚拟内存后,会导致硬盘I/O性能下降,因此,平台提供的Windows弹性云服务器默认未配置虚拟内存。如果弹性云服务器内存不足,建议通过“变更规格”操作来扩大内存。如果业务需要,必须开启虚拟内存,请参见本节内容进行配置。内存使用率已经非常高,同时I/O性能也不是很好的情况下,如果配置虚拟内存会起到反向效果:不仅系统性能提升Windows场景中...

weixin_36085895的博客 237

服务器虚拟内存c盘,服务器虚拟内存一般设置多少

服务器虚拟内存一般设置多少 内容精选换一换对象存储调优主要分为:冷存储配置调优所有数据盘都是机械硬盘(HDD)的场景,即DB/WAL分区、元数据存储池都使用机械硬盘所有数据盘都是机械硬盘(HDD)的场景,即DB/WAL分区、元数据存储池都使用机械硬盘均衡型配置调优以机械硬盘(HDD)作为数据盘,并配置适量固态硬盘(SSD)作为DB/WAL分区、元数据存储池的场景以机械硬盘(HDD在性能优化时,需要...

weixin_28916013的博客 495

服务器上虚拟内存怎么设置方法,服务器的虚拟内存设置方法

服务器的虚拟内存设置方法 内容精选换一换登录Windows云服务器时,系统报错,错误代码为“0x112f”,如图1所示。云服务器内存不足。方法一(推荐):变更规格,升级云服务器的CPU、内存大小。变更规格的方法,请参见变更规格(CPU和内存)。变更规格,升级云服务器的CPU、内存大小。变更规格的方法,请参见变更规格(CPU和内存)。方法二:开启虚拟内存,获取云服务器的空闲内存。对于不同的硬件设备,...

weixin_31937319的博客 1138

进程切换与线程切换的区别

注意这个题目问的是进程切换与线程切换的区别,不是进程与线程的区别。当然这里的线程指的是同一个进程中的线程。 这个问题能很好的考察面试者对进程和线程的理解深度,有比较高的区分度。 一、虚拟内存解放生产力 对于程序员来说,我们在编程时实际上是不怎么操心内存问题的,对于使用Java、Python、JavaScript等动态类型语言的程序员来说更是如此,自动内存回收机制的引入使得使用这类语言的程序员几乎完全不用关心内存问题;即使对于编译型语言C/C++来说,...

dongcheng_2015的博客 376

线程栈空间的大小

http://blog.youkuaiyun.com/cyblueboy83/article/details/7645331 一直做Windows服务器向Linux平台的移植工作,对于线程的栈空间也是似懂非懂,因而出现了一些问题和总结了部分经验,供大家分享。 在我的服务器上启动了286个线程后,其后的线程启动失败了,返回的错误原因是12,经查找定义如下: #define    ENOMEM  

zb872676223的专栏 1446

多线程 线程池 sock IO复用

1)iocp编程小结 http://www.cnblogs.com/Hybird3D/archive/2012/02/02/2335000.html http://www.cnblogs.com/Hybird3D/archive/2012/02/04/2337652.html     单个进程最大线程数 转自:http://jackyhongvip.iteye.

dong_cc的专栏 828
上一篇: 5000字详细解说什么是面向对象编程的三⼤特性—封装、继承、多态
下一篇: 盘点解析SpringIOC : BeanDefinition 的加载流程
Java_LingFeng
博客等级 码龄4年 505粉丝 259原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值