diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 859e271e26c661a88a726456bcaf128d61f83672..b127f4f529d409ec9aaa0efcfa5312e13a929b06 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -446,6 +446,177 @@ class LLSingleton : public LLSingletonBase
         return sData.mInitState == DELETED;
     }
 
+protected:
+    static EInitState getInitState()
+    {
+        return sData.mInitState;
+    }
+
+private:
+    struct SingletonData
+    {
+        // explicitly has a default constructor so that member variables are zero initialized in BSS
+        // and only changed by singleton logic, not constructor running during startup
+        EInitState      mInitState;
+        DERIVED_TYPE*   mInstance;
+    };
+    static SingletonData sData;
+};
+
+
+template <typename DERIVED_TYPE>
+class LLParamSingleton : public LLSingletonBase
+{
+private:
+
+    template <typename... Args>
+    static DERIVED_TYPE* constructSingleton(Args&&... args)
+    {
+        return new DERIVED_TYPE(std::forward<Args>(args)...);
+    }
+
+    // We know of no way to instruct the compiler that every subclass
+    // constructor MUST be private.
+    // However, we can make the LLPARAMSINGLETON() macro both declare
+    // a private constructor and provide the required friend declaration.
+    // How can we ensure that every subclass uses LLPARAMSINGLETON()?
+    // By making that macro provide a definition for this pure virtual
+    // method. If you get "can't instantiate class due to missing pure
+    // virtual method" for this method, then add LLPARAMSINGLETON(yourclass)
+    // in the subclass body.
+    virtual void you_must_use_LLSINGLETON_macro() = 0;
+
+protected:
+    // Pass DERIVED_TYPE explicitly to LLSingletonBase's constructor because,
+    // until our subclass constructor completes, *this isn't yet a
+    // full-fledged DERIVED_TYPE.
+    LLParamSingleton() : LLSingletonBase(LLSingletonBase::tag<DERIVED_TYPE>())
+    {
+        // populate base-class function pointer with the static
+        // deleteSingleton() function for this particular specialization
+        mDeleteSingleton = &deleteSingleton;
+
+        // add this new instance to the master list
+        LLSingleton_manage_master<DERIVED_TYPE>().add(this);
+    }
+
+public:
+
+    virtual ~LLParamSingleton()
+    {
+        // remove this instance from the master list
+        LLSingleton_manage_master<DERIVED_TYPE>().remove(this);
+        sData.mInstance = NULL;
+        sData.mInitState = DELETED;
+    }
+
+    // Passes arguments to DERIVED_TYPE's constructor and sets apropriate states
+    template <typename... Args>
+    static void initParamSingleton(Args&&... args)
+    {
+        sData.mInitState = CONSTRUCTING;
+        sData.mInstance = constructSingleton(std::forward<Args>(args)...);
+        sData.mInitState = INITIALIZED;
+        // initialize singleton after constructing it so that it can
+        // reference other singletons which in turn depend on it, thus
+        // breaking cyclic dependencies
+        sData.mInstance->initSingleton();
+        // pop this off stack of initializing singletons
+        LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sData.mInstance);
+    }
+
+    /**
+    * @brief Immediately delete the singleton.
+    *
+    * A subsequent call to LLProxy::getInstance() will construct a new
+    * instance of the class.
+    *
+    * Without an explicit call to LLSingletonBase::deleteAll(), LLSingletons
+    * are implicitly destroyed after main() has exited and the C++ runtime is
+    * cleaning up statically-constructed objects. Some classes derived from
+    * LLSingleton have objects that are part of a runtime system that is
+    * terminated before main() exits. Calling the destructor of those objects
+    * after the termination of their respective systems can cause crashes and
+    * other problems during termination of the project. Using this method to
+    * destroy the singleton early can prevent these crashes.
+    *
+    * An example where this is needed is for a LLSingleton that has an APR
+    * object as a member that makes APR calls on destruction. The APR system is
+    * shut down explicitly before main() exits. This causes a crash on exit.
+    * Using this method before the call to apr_terminate() and NOT calling
+    * getInstance() again will prevent the crash.
+    */
+    static void deleteSingleton()
+    {
+        delete sData.mInstance;
+        sData.mInstance = NULL;
+        sData.mInitState = DELETED;
+    }
+
+    static DERIVED_TYPE* getInstance()
+    {
+        switch (sData.mInitState)
+        {
+        case UNINITIALIZED:
+            logerrs("Uninitialized param singleton ",
+                demangle(typeid(DERIVED_TYPE).name()).c_str());
+            return NULL;
+
+        case CONSTRUCTING:
+            logerrs("Tried to access singleton ",
+                demangle(typeid(DERIVED_TYPE).name()).c_str(),
+                " from singleton constructor!");
+            return NULL;
+
+        case INITIALIZING:
+            logerrs("State not supported by ",
+                demangle(typeid(DERIVED_TYPE).name()).c_str(),
+                " since it is a parametric singleton!");
+            break;
+
+        case INITIALIZED:
+            break;
+
+        case DELETED:
+            logerrs("Trying to access deleted param singleton ",
+                demangle(typeid(DERIVED_TYPE).name()).c_str());
+
+            break;
+        }
+
+        // By this point, if DERIVED_TYPE was pushed onto the initializing
+        // stack, it has been popped off. So the top of that stack, if any, is
+        // an LLSingleton that directly depends on DERIVED_TYPE. If this call
+        // came from another LLSingleton, rather than from vanilla application
+        // code, record the dependency.
+        sData.mInstance->capture_dependency(
+            LLSingleton_manage_master<DERIVED_TYPE>().get_initializing(sData.mInstance),
+            sData.mInitState);
+        return sData.mInstance;
+    }
+
+    // Reference version of getInstance()
+    // Preferred over getInstance() as it disallows checking for NULL
+    static DERIVED_TYPE& instance()
+    {
+        return *getInstance();
+    }
+
+    // Has this singleton been created yet?
+    // Use this to avoid accessing singletons before they can safely be constructed.
+    static bool instanceExists()
+    {
+        return sData.mInitState == INITIALIZED;
+    }
+
+    // Has this singleton been deleted? This can be useful during shutdown
+    // processing to avoid "resurrecting" a singleton we thought we'd already
+    // cleaned up.
+    static bool wasDeleted()
+    {
+        return sData.mInitState == DELETED;
+    }
+
 private:
     struct SingletonData
     {
@@ -460,6 +631,9 @@ class LLSingleton : public LLSingletonBase
 template<typename T>
 typename LLSingleton<T>::SingletonData LLSingleton<T>::sData;
 
+template<typename T>
+typename LLParamSingleton<T>::SingletonData LLParamSingleton<T>::sData;
+
 /**
  * Use LLSINGLETON(Foo); at the start of an LLSingleton<Foo> subclass body
  * when you want to declare an out-of-line constructor:
@@ -510,4 +684,35 @@ private:                                                                \
     /* LLSINGLETON() is carefully implemented to permit exactly this */ \
     LLSINGLETON(DERIVED_CLASS) {}
 
+/**
+* Use LLPARAMSINGLETON(Foo); at the start of an LLParamSingleton<Foo> subclass body
+* when you want to declare an out-of-line constructor:
+*
+* @code
+*   class Foo: public LLParamSingleton<Foo>
+*   {
+*       // use this macro at start of every LLSingleton subclass
+*       LLPARAMSINGLETON(Foo);
+*   public:
+*       // ...
+*   };
+*   // ...
+*   [inline]
+*   Foo::Foo() { ... }
+* @endcode
+*
+* Unfortunately, this mechanism does not permit you to define even a simple
+* (but nontrivial) constructor within the class body. Use LLPARAMSINGLETON()
+* and define the constructor outside the class body. If you must define it
+* in a header file, use 'inline' (unless it's a template class) to avoid
+* duplicate-symbol errors at link time.
+*/
+#define LLPARAMSINGLETON(DERIVED_CLASS, ...)                                      \
+private:                                                                \
+    /* implement LLSingleton pure virtual method whose sole purpose */  \
+    /* is to remind people to use this macro */                         \
+    virtual void you_must_use_LLSINGLETON_macro() {}                    \
+    friend class LLParamSingleton<DERIVED_CLASS>;                            \
+    DERIVED_CLASS(__VA_ARGS__)
+
 #endif
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
index 680fbf548f7ffd80ac819cda04957e027592088b..9dea87611423e5ccfdeac6ac7354cd1df4f4f230 100644
--- a/indra/llimage/llimage.cpp
+++ b/indra/llimage/llimage.cpp
@@ -583,39 +583,29 @@ static void bilinear_scale(const U8 *src, U32 srcW, U32 srcH, U32 srcCh, U32 src
 // LLImage
 //---------------------------------------------------------------------------
 
-//static
-std::string LLImage::sLastErrorMessage;
-LLMutex* LLImage::sMutex = NULL;
-bool LLImage::sUseNewByteRange = false;
-S32  LLImage::sMinimalReverseByteRangePercent = 75;
-
-//static
-void LLImage::initClass(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent)
+LLImage::LLImage(bool use_new_byte_range, S32 minimal_reverse_byte_range_percent)
 {
-	sUseNewByteRange = use_new_byte_range;
-    sMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent;
-	sMutex = new LLMutex();
+    mMutex = new LLMutex();
+    mUseNewByteRange = use_new_byte_range;
+    mMinimalReverseByteRangePercent = minimal_reverse_byte_range_percent;
 }
 
-//static
-void LLImage::cleanupClass()
+LLImage::~LLImage()
 {
-	delete sMutex;
-	sMutex = NULL;
+    delete mMutex;
+    mMutex = NULL;
 }
 
-//static
-const std::string& LLImage::getLastError()
+const std::string& LLImage::getLastErrorMessage()
 {
 	static const std::string noerr("No Error");
-	return sLastErrorMessage.empty() ? noerr : sLastErrorMessage;
+	return mLastErrorMessage.empty() ? noerr : mLastErrorMessage;
 }
 
-//static
-void LLImage::setLastError(const std::string& message)
+void LLImage::setLastErrorMessage(const std::string& message)
 {
-	LLMutexLock m(sMutex);
-	sLastErrorMessage = message;
+	LLMutexLock m(mMutex);
+	mLastErrorMessage = message;
 }
 
 //---------------------------------------------------------------------------
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
index 8ec49d3f0f96cb806b08440a212da8f91ee7818e..e5526ba9c0c6d51d6328aeedf3189197298fb80e 100644
--- a/indra/llimage/llimage.h
+++ b/indra/llimage/llimage.h
@@ -30,6 +30,7 @@
 #include "lluuid.h"
 #include "llstring.h"
 #include "llpointer.h"
+#include "llsingleton.h"
 #include "lltrace.h"
 
 const S32 MIN_IMAGE_MIP =  2; // 4x4, only used for expand/contract power of 2
@@ -88,23 +89,25 @@ typedef enum e_image_codec
 //============================================================================
 // library initialization class
 
-class LLImage
+class LLImage : public LLParamSingleton<LLImage>
 {
+	LLPARAMSINGLETON(LLImage, bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75);
+	~LLImage();
 public:
-	static void initClass(bool use_new_byte_range = false, S32 minimal_reverse_byte_range_percent = 75);
-	static void cleanupClass();
 
-	static const std::string& getLastError();
-	static void setLastError(const std::string& message);
-	
-	static bool useNewByteRange() { return sUseNewByteRange; }
-    static S32  getReverseByteRangePercent() { return sMinimalReverseByteRangePercent; }
-	
-protected:
-	static LLMutex* sMutex;
-	static std::string sLastErrorMessage;
-	static bool sUseNewByteRange;
-    static S32  sMinimalReverseByteRangePercent;
+	const std::string& getLastErrorMessage();
+	static const std::string& getLastError() { return getInstance()->getLastErrorMessage(); };
+	void setLastErrorMessage(const std::string& message);
+	static void setLastError(const std::string& message) { getInstance()->setLastErrorMessage(message); }
+
+	bool useNewByteRange() { return mUseNewByteRange; }
+	S32  getReverseByteRangePercent() { return mMinimalReverseByteRangePercent; }
+
+private:
+	LLMutex* mMutex;
+	std::string mLastErrorMessage;
+	bool mUseNewByteRange;
+	S32  mMinimalReverseByteRangePercent;
 };
 
 //============================================================================
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
index 4bff21610fc83c861fa301f6ce189823e0d1080b..71cab0554d78943faf8faf17a60f101c56c35ce6 100644
--- a/indra/llimage/llimagej2c.cpp
+++ b/indra/llimage/llimagej2c.cpp
@@ -281,7 +281,7 @@ S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 r
 	S32 bytes;
 	S32 new_bytes = (S32) (sqrt((F32)(w*h))*(F32)(comp)*rate*1000.f/layer_factor);
 	S32 old_bytes = (S32)((F32)(w*h*comp)*rate);
-	bytes = (LLImage::useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes);
+	bytes = (LLImage::getInstance()->useNewByteRange() && (new_bytes < old_bytes) ? new_bytes : old_bytes);
 	bytes = llmax(bytes, calcHeaderSizeJ2C());
 	return bytes;
 }
@@ -322,7 +322,7 @@ S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
 	{
 		S32 bytes_needed = calcDataSize(discard_level);
 		// Use TextureReverseByteRange percent (see settings.xml) of the optimal size to qualify as correct rendering for the given discard level
-		if (bytes >= (bytes_needed*LLImage::getReverseByteRangePercent()/100))
+		if (bytes >= (bytes_needed*LLImage::getInstance()->getReverseByteRangePercent()/100))
 		{
 			break;
 		}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 0435c3d3989db51c250516a1e7de0fa9854d56df..cb4655cd874a63cd8d62e72cedc2850673c02fa2 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -2040,7 +2040,6 @@ bool LLAppViewer::cleanup()
 	LLUIImageList::getInstance()->cleanUp();
 
 	// This should eventually be done in LLAppViewer
-	SUBSYSTEM_CLEANUP(LLImage);
 	SUBSYSTEM_CLEANUP(LLVFSThread);
 	SUBSYSTEM_CLEANUP(LLLFSThread);
 
@@ -2148,7 +2147,7 @@ bool LLAppViewer::initThreads()
 {
 	static const bool enable_threads = true;
 
-	LLImage::initClass(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange"));
+	LLImage::initParamSingleton(gSavedSettings.getBOOL("TextureNewByteRange"),gSavedSettings.getS32("TextureReverseByteRange"));
 
 	LLVFSThread::initClass(enable_threads && false);
 	LLLFSThread::initClass(enable_threads && false);
diff --git a/indra/newview/llconversationlog.h b/indra/newview/llconversationlog.h
index 035cbcb9451e7eb41be8747176b15ece750f84eb..38247f8eff699c22779541075a683f17281d9323 100644
--- a/indra/newview/llconversationlog.h
+++ b/indra/newview/llconversationlog.h
@@ -107,11 +107,10 @@ class LLConversation
  * To distinguish two conversations with the same sessionID it's also needed to compare their creation date.
  */
 
-class LLConversationLog : public LLSingleton<LLConversationLog>, LLIMSessionObserver
+class LLConversationLog : public LLParamSingleton<LLConversationLog>, LLIMSessionObserver
 {
-	LLSINGLETON(LLConversationLog);
+	LLPARAMSINGLETON(LLConversationLog);
 public:
-
 	void removeConversation(const LLConversation& conversation);
 
 	/**
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 7f9f0da315cc7a11d381510aa559efb7555f59fe..82e1d6be4aa97bbfde912b99af9d033e4f3789cd 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -1289,7 +1289,7 @@ bool idle_startup()
 		display_startup();
 		LLStartUp::setStartupState( STATE_MULTIMEDIA_INIT );
 		
-		LLConversationLog::getInstance();
+		LLConversationLog::initParamSingleton();
 
 		return FALSE;
 	}